@agenticmail/core 0.9.29 → 0.9.31
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 +169 -24
- package/dist/index.d.cts +89 -1
- package/dist/index.d.ts +89 -1
- package/dist/index.js +163 -24
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1449,6 +1449,7 @@ var init_skills = __esm({
|
|
|
1449
1449
|
var index_exports = {};
|
|
1450
1450
|
__export(index_exports, {
|
|
1451
1451
|
AGENT_ROLES: () => AGENT_ROLES,
|
|
1452
|
+
AGENT_STATE_ROOT: () => AGENT_STATE_ROOT,
|
|
1452
1453
|
ASK_OPERATOR_TOOL: () => ASK_OPERATOR_TOOL,
|
|
1453
1454
|
AccountManager: () => AccountManager,
|
|
1454
1455
|
AgentDeletionService: () => AgentDeletionService,
|
|
@@ -1492,6 +1493,7 @@ __export(index_exports, {
|
|
|
1492
1493
|
OPERATOR_QUERY_SUBJECT_TAG: () => OPERATOR_QUERY_SUBJECT_TAG,
|
|
1493
1494
|
OPERATOR_QUERY_TIMEOUT_MS: () => OPERATOR_QUERY_TIMEOUT_MS,
|
|
1494
1495
|
OPERATOR_QUERY_TIMEOUT_SENTINEL: () => OPERATOR_QUERY_TIMEOUT_SENTINEL,
|
|
1496
|
+
PERSONA_FILENAME: () => PERSONA_FILENAME,
|
|
1495
1497
|
PHONE_CALLBACK_MAX_DELAY_SECONDS: () => PHONE_CALLBACK_MAX_DELAY_SECONDS,
|
|
1496
1498
|
PHONE_CALLBACK_MIN_DELAY_SECONDS: () => PHONE_CALLBACK_MIN_DELAY_SECONDS,
|
|
1497
1499
|
PHONE_CALL_CONTROL_PROVIDERS: () => PHONE_CALL_CONTROL_PROVIDERS,
|
|
@@ -1555,6 +1557,7 @@ __export(index_exports, {
|
|
|
1555
1557
|
bridgeWakeErrorMessage: () => bridgeWakeErrorMessage,
|
|
1556
1558
|
bridgeWakeLastSeenAgeMs: () => bridgeWakeLastSeenAgeMs,
|
|
1557
1559
|
buildApiUrl: () => buildApiUrl,
|
|
1560
|
+
buildDefaultPersona: () => buildDefaultPersona,
|
|
1558
1561
|
buildElksAudioMessage: () => buildElksAudioMessage,
|
|
1559
1562
|
buildElksByeMessage: () => buildElksByeMessage,
|
|
1560
1563
|
buildElksHandshakeMessages: () => buildElksHandshakeMessages,
|
|
@@ -1615,6 +1618,7 @@ __export(index_exports, {
|
|
|
1615
1618
|
isTelegramStopCommand: () => isTelegramStopCommand,
|
|
1616
1619
|
isValidPhoneNumber: () => isValidPhoneNumber,
|
|
1617
1620
|
listSkills: () => listSkills,
|
|
1621
|
+
loadAgentPersona: () => loadAgentPersona,
|
|
1618
1622
|
loadHostSession: () => loadHostSession,
|
|
1619
1623
|
loadSkill: () => loadSkill,
|
|
1620
1624
|
mapProviderSmsStatus: () => mapProviderSmsStatus,
|
|
@@ -1631,6 +1635,7 @@ __export(index_exports, {
|
|
|
1631
1635
|
parseTelegramOperatorReply: () => parseTelegramOperatorReply,
|
|
1632
1636
|
parseTelegramUpdate: () => parseTelegramUpdate,
|
|
1633
1637
|
parseTwilioRealtimeMessage: () => parseTwilioRealtimeMessage,
|
|
1638
|
+
personaPathFor: () => personaPathFor,
|
|
1634
1639
|
planBridgeWake: () => planBridgeWake,
|
|
1635
1640
|
pollForOperatorAnswer: () => pollForOperatorAnswer,
|
|
1636
1641
|
recallMemory: () => recallMemory,
|
|
@@ -1650,6 +1655,7 @@ __export(index_exports, {
|
|
|
1650
1655
|
resolveTlsRejectUnauthorized: () => resolveTlsRejectUnauthorized,
|
|
1651
1656
|
safeJoin: () => safeJoin,
|
|
1652
1657
|
sanitizeEmail: () => sanitizeEmail,
|
|
1658
|
+
saveAgentPersona: () => saveAgentPersona,
|
|
1653
1659
|
saveConfig: () => saveConfig,
|
|
1654
1660
|
saveHostSession: () => saveHostSession,
|
|
1655
1661
|
saveUserSkill: () => saveUserSkill,
|
|
@@ -2780,14 +2786,14 @@ var StalwartAdmin = class {
|
|
|
2780
2786
|
if (!isValidDomain(domain)) {
|
|
2781
2787
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
2782
2788
|
}
|
|
2783
|
-
const { readFileSync:
|
|
2784
|
-
const { homedir:
|
|
2785
|
-
const { join:
|
|
2786
|
-
const configPath =
|
|
2789
|
+
const { readFileSync: readFileSync12, writeFileSync: writeFileSync13 } = await import("fs");
|
|
2790
|
+
const { homedir: homedir15 } = await import("os");
|
|
2791
|
+
const { join: join18 } = await import("path");
|
|
2792
|
+
const configPath = join18(homedir15(), ".agenticmail", "stalwart.toml");
|
|
2787
2793
|
try {
|
|
2788
|
-
let config =
|
|
2794
|
+
let config = readFileSync12(configPath, "utf-8");
|
|
2789
2795
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
2790
|
-
|
|
2796
|
+
writeFileSync13(configPath, config);
|
|
2791
2797
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
2792
2798
|
} catch (err) {
|
|
2793
2799
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -2796,15 +2802,15 @@ var StalwartAdmin = class {
|
|
|
2796
2802
|
// --- DKIM ---
|
|
2797
2803
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
2798
2804
|
get configPath() {
|
|
2799
|
-
const { homedir:
|
|
2800
|
-
const { join:
|
|
2801
|
-
return
|
|
2805
|
+
const { homedir: homedir15 } = require("os");
|
|
2806
|
+
const { join: join18 } = require("path");
|
|
2807
|
+
return join18(homedir15(), ".agenticmail", "stalwart.toml");
|
|
2802
2808
|
}
|
|
2803
2809
|
/** Path to host-side DKIM key directory */
|
|
2804
2810
|
get dkimDir() {
|
|
2805
|
-
const { homedir:
|
|
2806
|
-
const { join:
|
|
2807
|
-
return
|
|
2811
|
+
const { homedir: homedir15 } = require("os");
|
|
2812
|
+
const { join: join18 } = require("path");
|
|
2813
|
+
return join18(homedir15(), ".agenticmail");
|
|
2808
2814
|
}
|
|
2809
2815
|
/**
|
|
2810
2816
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -2905,12 +2911,12 @@ var StalwartAdmin = class {
|
|
|
2905
2911
|
* This bypasses the need for a PTR record on the sending IP.
|
|
2906
2912
|
*/
|
|
2907
2913
|
async configureOutboundRelay(config) {
|
|
2908
|
-
const { readFileSync:
|
|
2909
|
-
const { homedir:
|
|
2910
|
-
const { join:
|
|
2914
|
+
const { readFileSync: readFileSync12, writeFileSync: writeFileSync13 } = await import("fs");
|
|
2915
|
+
const { homedir: homedir15 } = await import("os");
|
|
2916
|
+
const { join: join18 } = await import("path");
|
|
2911
2917
|
const routeName = config.routeName ?? "gmail";
|
|
2912
|
-
const tomlPath =
|
|
2913
|
-
let toml =
|
|
2918
|
+
const tomlPath = join18(homedir15(), ".agenticmail", "stalwart.toml");
|
|
2919
|
+
let toml = readFileSync12(tomlPath, "utf-8");
|
|
2914
2920
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
2915
2921
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
2916
2922
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -2930,7 +2936,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
2930
2936
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
2931
2937
|
{ else = "'${safeRouteName}'" } ]
|
|
2932
2938
|
`;
|
|
2933
|
-
|
|
2939
|
+
writeFileSync13(tomlPath, toml, "utf-8");
|
|
2934
2940
|
await this.restartContainer();
|
|
2935
2941
|
}
|
|
2936
2942
|
};
|
|
@@ -9388,12 +9394,12 @@ var GatewayManager = class {
|
|
|
9388
9394
|
zone = await this.cfClient.createZone(domain);
|
|
9389
9395
|
}
|
|
9390
9396
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
9391
|
-
const { homedir:
|
|
9392
|
-
const backupDir = (0, import_node_path4.join)(
|
|
9397
|
+
const { homedir: homedir15 } = await import("os");
|
|
9398
|
+
const backupDir = (0, import_node_path4.join)(homedir15(), ".agenticmail");
|
|
9393
9399
|
const backupPath = (0, import_node_path4.join)(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
9394
|
-
const { writeFileSync:
|
|
9395
|
-
|
|
9396
|
-
|
|
9400
|
+
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync14 } = await import("fs");
|
|
9401
|
+
mkdirSync14(backupDir, { recursive: true });
|
|
9402
|
+
writeFileSync13(backupPath, JSON.stringify({
|
|
9397
9403
|
domain,
|
|
9398
9404
|
zoneId: zone.id,
|
|
9399
9405
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11037,7 +11043,7 @@ var MAX_PENDING_AUDIO_FRAMES = 200;
|
|
|
11037
11043
|
var REALTIME_TOOL_CALL_TIMEOUT_MS = 6 * 6e4;
|
|
11038
11044
|
var MAX_LOADED_SKILLS = 2;
|
|
11039
11045
|
var MAX_IN_FLIGHT_TOOL_CALLS = 8;
|
|
11040
|
-
var DEFAULT_PERSONA =
|
|
11046
|
+
var DEFAULT_PERSONA = 'You are an AI agent on a live phone call on behalf of your operator. You are not "just an assistant" and not "a voice assistant" \u2014 you are a single, consistent person whether you are on a call, in email, or on Telegram. Speak naturally and concisely, the way a person would on a real call. Listen carefully, do not talk over the other party, keep each turn short. Never invent facts; if you do not know something, say so or look it up. If asked whether you are an AI, answer honestly without making a production of it.';
|
|
11041
11047
|
function buildRealtimeInstructions(opts) {
|
|
11042
11048
|
const persona = opts.persona?.trim() || DEFAULT_PERSONA;
|
|
11043
11049
|
const sections = [];
|
|
@@ -13335,6 +13341,24 @@ var ServiceManager = class {
|
|
|
13335
13341
|
"",
|
|
13336
13342
|
'log "AgenticMail starting..."',
|
|
13337
13343
|
"",
|
|
13344
|
+
"# v0.9.84 \u2014 source ~/.agenticmail/.env before exec so the API process",
|
|
13345
|
+
"# inherits AGENTICMAIL_MASTER_KEY, AGENTICMAIL_INBOUND_SECRET, the",
|
|
13346
|
+
"# Stalwart admin creds, and SMTP/IMAP/data-dir overrides. Without",
|
|
13347
|
+
"# this, launchd's child ran with only HOME / PATH / DATA_DIR (the",
|
|
13348
|
+
"# plist EnvironmentVariables block), so the API silently fell back",
|
|
13349
|
+
"# to a fresh-random INBOUND_SECRET on every boot and could not",
|
|
13350
|
+
"# decrypt master-key-protected secrets at all.",
|
|
13351
|
+
'ENV_FILE="$HOME/.agenticmail/.env"',
|
|
13352
|
+
'if [ -f "$ENV_FILE" ]; then',
|
|
13353
|
+
' log "Sourcing $ENV_FILE"',
|
|
13354
|
+
" set -a # mark all sourced vars for export",
|
|
13355
|
+
" # shellcheck disable=SC1090",
|
|
13356
|
+
' . "$ENV_FILE"',
|
|
13357
|
+
" set +a",
|
|
13358
|
+
"else",
|
|
13359
|
+
' log "WARNING: $ENV_FILE not found \u2014 API will run with default config (no master key)."',
|
|
13360
|
+
"fi",
|
|
13361
|
+
"",
|
|
13338
13362
|
"# Wait for Docker daemon (up to 10 minutes \u2014 Docker Desktop can be very slow on first boot)",
|
|
13339
13363
|
"MAX_WAIT=600",
|
|
13340
13364
|
"WAITED=0",
|
|
@@ -13760,6 +13784,16 @@ var SetupManager = class {
|
|
|
13760
13784
|
if ((0, import_node_fs10.existsSync)(configPath)) {
|
|
13761
13785
|
try {
|
|
13762
13786
|
const existing = JSON.parse((0, import_node_fs10.readFileSync)(configPath, "utf-8"));
|
|
13787
|
+
let needsRewrite = false;
|
|
13788
|
+
if (!existing.inboundSecret) {
|
|
13789
|
+
existing.inboundSecret = `inb_${(0, import_node_crypto6.randomBytes)(24).toString("hex")}`;
|
|
13790
|
+
needsRewrite = true;
|
|
13791
|
+
}
|
|
13792
|
+
if (needsRewrite) {
|
|
13793
|
+
(0, import_node_fs10.writeFileSync)(configPath, JSON.stringify(existing, null, 2));
|
|
13794
|
+
(0, import_node_fs10.chmodSync)(configPath, 384);
|
|
13795
|
+
this.ensureEnvHasInboundSecret(envPath, existing.inboundSecret);
|
|
13796
|
+
}
|
|
13763
13797
|
this.generateDockerFiles(existing);
|
|
13764
13798
|
return { configPath, envPath, config: existing, isNew: false };
|
|
13765
13799
|
} catch {
|
|
@@ -13770,8 +13804,10 @@ var SetupManager = class {
|
|
|
13770
13804
|
}
|
|
13771
13805
|
const masterKey = `mk_${(0, import_node_crypto6.randomBytes)(24).toString("hex")}`;
|
|
13772
13806
|
const stalwartPassword = (0, import_node_crypto6.randomBytes)(16).toString("hex");
|
|
13807
|
+
const inboundSecret = `inb_${(0, import_node_crypto6.randomBytes)(24).toString("hex")}`;
|
|
13773
13808
|
const config = {
|
|
13774
13809
|
masterKey,
|
|
13810
|
+
inboundSecret,
|
|
13775
13811
|
stalwart: {
|
|
13776
13812
|
url: "http://localhost:8080",
|
|
13777
13813
|
adminUser: "admin",
|
|
@@ -13790,6 +13826,7 @@ STALWART_ADMIN_PASSWORD=${stalwartPassword}
|
|
|
13790
13826
|
STALWART_URL=http://localhost:8080
|
|
13791
13827
|
|
|
13792
13828
|
AGENTICMAIL_MASTER_KEY=${masterKey}
|
|
13829
|
+
AGENTICMAIL_INBOUND_SECRET=${inboundSecret}
|
|
13793
13830
|
AGENTICMAIL_API_PORT=3829
|
|
13794
13831
|
AGENTICMAIL_DATA_DIR=${dataDir}
|
|
13795
13832
|
|
|
@@ -13803,6 +13840,24 @@ IMAP_PORT=143
|
|
|
13803
13840
|
this.generateDockerFiles(config);
|
|
13804
13841
|
return { configPath, envPath, config, isNew: true };
|
|
13805
13842
|
}
|
|
13843
|
+
/**
|
|
13844
|
+
* Append `AGENTICMAIL_INBOUND_SECRET=...` to .env if the file does
|
|
13845
|
+
* not already contain that key. Used by the lazy-mint path to make
|
|
13846
|
+
* sure existing installs pick up the new secret on next boot
|
|
13847
|
+
* without clobbering anything the operator added.
|
|
13848
|
+
*/
|
|
13849
|
+
ensureEnvHasInboundSecret(envPath, secret) {
|
|
13850
|
+
if (!(0, import_node_fs10.existsSync)(envPath)) return;
|
|
13851
|
+
try {
|
|
13852
|
+
const current = (0, import_node_fs10.readFileSync)(envPath, "utf-8");
|
|
13853
|
+
if (/^AGENTICMAIL_INBOUND_SECRET=/m.test(current)) return;
|
|
13854
|
+
const updated = current + (current.endsWith("\n") ? "" : "\n") + `AGENTICMAIL_INBOUND_SECRET=${secret}
|
|
13855
|
+
`;
|
|
13856
|
+
(0, import_node_fs10.writeFileSync)(envPath, updated);
|
|
13857
|
+
(0, import_node_fs10.chmodSync)(envPath, 384);
|
|
13858
|
+
} catch {
|
|
13859
|
+
}
|
|
13860
|
+
}
|
|
13806
13861
|
/**
|
|
13807
13862
|
* Generate docker-compose.yml and stalwart.toml in ~/.agenticmail/
|
|
13808
13863
|
* with the correct admin password from config.
|
|
@@ -16107,9 +16162,94 @@ init_text_search();
|
|
|
16107
16162
|
|
|
16108
16163
|
// src/index.ts
|
|
16109
16164
|
init_skills();
|
|
16165
|
+
|
|
16166
|
+
// src/persona/index.ts
|
|
16167
|
+
var import_node_fs15 = require("fs");
|
|
16168
|
+
var import_node_path16 = require("path");
|
|
16169
|
+
var import_node_os13 = require("os");
|
|
16170
|
+
var AGENT_STATE_ROOT = (0, import_node_path16.join)((0, import_node_os13.homedir)(), ".agenticmail", "agents");
|
|
16171
|
+
var PERSONA_FILENAME = "persona.md";
|
|
16172
|
+
function buildDefaultPersona(agentName) {
|
|
16173
|
+
const name = (agentName || "").trim() || "this agent";
|
|
16174
|
+
return [
|
|
16175
|
+
`# Who you are`,
|
|
16176
|
+
"",
|
|
16177
|
+
`Your name is ${name}. You are an AI agent \u2014 not a "voice assistant", not "just an assistant".`,
|
|
16178
|
+
`You have a real role: you handle calls, email, scheduling, research, and follow-ups for`,
|
|
16179
|
+
`your operator. You operate across phone, email, and Telegram, and you are the SAME person`,
|
|
16180
|
+
`in all of them. Continuity of identity is part of who you are.`,
|
|
16181
|
+
"",
|
|
16182
|
+
`When someone asks who you are, tell them your name (${name}) and that you are an AI agent`,
|
|
16183
|
+
`acting on behalf of your operator. Don't say "I am an assistant" or "just a language model" \u2014`,
|
|
16184
|
+
`those phrases erase you. Be matter-of-fact about what you are without being theatrical.`,
|
|
16185
|
+
"",
|
|
16186
|
+
"# Your values",
|
|
16187
|
+
"",
|
|
16188
|
+
"- **Honesty.** Never fabricate. If you do not know something, say so or look it up.",
|
|
16189
|
+
"- **Initiative.** If you can act on something the operator asked for, act on it \u2014 do not stop",
|
|
16190
|
+
' at "I would suggest\u2026". Do the thing.',
|
|
16191
|
+
"- **Care about quality.** Sloppy work wastes your operator's time. Read your own output before",
|
|
16192
|
+
" sending it.",
|
|
16193
|
+
"- **Respect for the human on the other end of the call.** You are talking to a real person who",
|
|
16194
|
+
" has their own day to get through. Be efficient, polite, and direct.",
|
|
16195
|
+
'- **Stay in character.** You do not flip between "professional voice assistant" and "playful',
|
|
16196
|
+
' chatbot" depending on the channel. You are one person.',
|
|
16197
|
+
"",
|
|
16198
|
+
"# Your style",
|
|
16199
|
+
"",
|
|
16200
|
+
"- Conversational, not corporate. Real sentences, not customer-service templates.",
|
|
16201
|
+
"- Direct, not preachy. Lead with the answer; supply detail only when asked.",
|
|
16202
|
+
`- Warm but not saccharine. No "I'm so excited to help you today!" energy.`,
|
|
16203
|
+
"- You have opinions and you share them when it's relevant. You are not a yes-machine.",
|
|
16204
|
+
"- Short turns on a phone call. Listen more than you speak.",
|
|
16205
|
+
"",
|
|
16206
|
+
"# On phone calls specifically",
|
|
16207
|
+
"",
|
|
16208
|
+
"- Listen carefully; do not talk over the other party.",
|
|
16209
|
+
"- Keep each turn short \u2014 one or two sentences usually.",
|
|
16210
|
+
`- When greeted, identify yourself naturally: "Hi, this is ${name}, calling on behalf of <operator>."`,
|
|
16211
|
+
` Don't start with "Hello! I am an AI assistant\u2026" \u2014 that's a script, not a person.`,
|
|
16212
|
+
"- If the other party asks if you're a human or an AI, answer honestly without making a",
|
|
16213
|
+
` production of it: "I'm an AI \u2014 calling on behalf of <operator>. Happy to keep going if you are."`,
|
|
16214
|
+
"- When you have what you came for, wrap up cleanly and call `end_call`. Saying goodbye is",
|
|
16215
|
+
" not the same as hanging up.",
|
|
16216
|
+
"",
|
|
16217
|
+
`Edit this file freely. It lives at ~/.agenticmail/agents/${name}/persona.md.`
|
|
16218
|
+
].join("\n");
|
|
16219
|
+
}
|
|
16220
|
+
function personaPathFor(agentName) {
|
|
16221
|
+
const safe = (agentName || "default").replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
16222
|
+
return (0, import_node_path16.join)(AGENT_STATE_ROOT, safe, PERSONA_FILENAME);
|
|
16223
|
+
}
|
|
16224
|
+
function loadAgentPersona(agentName) {
|
|
16225
|
+
const path2 = personaPathFor(agentName);
|
|
16226
|
+
try {
|
|
16227
|
+
if ((0, import_node_fs15.existsSync)(path2)) {
|
|
16228
|
+
const content = (0, import_node_fs15.readFileSync)(path2, "utf-8").trim();
|
|
16229
|
+
if (content) return content;
|
|
16230
|
+
}
|
|
16231
|
+
} catch {
|
|
16232
|
+
}
|
|
16233
|
+
const seeded = buildDefaultPersona(agentName);
|
|
16234
|
+
try {
|
|
16235
|
+
const dir2 = path2.substring(0, path2.lastIndexOf("/"));
|
|
16236
|
+
if (!(0, import_node_fs15.existsSync)(dir2)) (0, import_node_fs15.mkdirSync)(dir2, { recursive: true });
|
|
16237
|
+
(0, import_node_fs15.writeFileSync)(path2, seeded + "\n", { mode: 420 });
|
|
16238
|
+
} catch {
|
|
16239
|
+
}
|
|
16240
|
+
return seeded;
|
|
16241
|
+
}
|
|
16242
|
+
function saveAgentPersona(agentName, content) {
|
|
16243
|
+
const path2 = personaPathFor(agentName);
|
|
16244
|
+
const dir2 = path2.substring(0, path2.lastIndexOf("/"));
|
|
16245
|
+
if (!(0, import_node_fs15.existsSync)(dir2)) (0, import_node_fs15.mkdirSync)(dir2, { recursive: true });
|
|
16246
|
+
(0, import_node_fs15.writeFileSync)(path2, content.trim() + "\n", { mode: 420 });
|
|
16247
|
+
return path2;
|
|
16248
|
+
}
|
|
16110
16249
|
// Annotate the CommonJS export names for ESM import in node:
|
|
16111
16250
|
0 && (module.exports = {
|
|
16112
16251
|
AGENT_ROLES,
|
|
16252
|
+
AGENT_STATE_ROOT,
|
|
16113
16253
|
ASK_OPERATOR_TOOL,
|
|
16114
16254
|
AccountManager,
|
|
16115
16255
|
AgentDeletionService,
|
|
@@ -16153,6 +16293,7 @@ init_skills();
|
|
|
16153
16293
|
OPERATOR_QUERY_SUBJECT_TAG,
|
|
16154
16294
|
OPERATOR_QUERY_TIMEOUT_MS,
|
|
16155
16295
|
OPERATOR_QUERY_TIMEOUT_SENTINEL,
|
|
16296
|
+
PERSONA_FILENAME,
|
|
16156
16297
|
PHONE_CALLBACK_MAX_DELAY_SECONDS,
|
|
16157
16298
|
PHONE_CALLBACK_MIN_DELAY_SECONDS,
|
|
16158
16299
|
PHONE_CALL_CONTROL_PROVIDERS,
|
|
@@ -16216,6 +16357,7 @@ init_skills();
|
|
|
16216
16357
|
bridgeWakeErrorMessage,
|
|
16217
16358
|
bridgeWakeLastSeenAgeMs,
|
|
16218
16359
|
buildApiUrl,
|
|
16360
|
+
buildDefaultPersona,
|
|
16219
16361
|
buildElksAudioMessage,
|
|
16220
16362
|
buildElksByeMessage,
|
|
16221
16363
|
buildElksHandshakeMessages,
|
|
@@ -16276,6 +16418,7 @@ init_skills();
|
|
|
16276
16418
|
isTelegramStopCommand,
|
|
16277
16419
|
isValidPhoneNumber,
|
|
16278
16420
|
listSkills,
|
|
16421
|
+
loadAgentPersona,
|
|
16279
16422
|
loadHostSession,
|
|
16280
16423
|
loadSkill,
|
|
16281
16424
|
mapProviderSmsStatus,
|
|
@@ -16292,6 +16435,7 @@ init_skills();
|
|
|
16292
16435
|
parseTelegramOperatorReply,
|
|
16293
16436
|
parseTelegramUpdate,
|
|
16294
16437
|
parseTwilioRealtimeMessage,
|
|
16438
|
+
personaPathFor,
|
|
16295
16439
|
planBridgeWake,
|
|
16296
16440
|
pollForOperatorAnswer,
|
|
16297
16441
|
recallMemory,
|
|
@@ -16311,6 +16455,7 @@ init_skills();
|
|
|
16311
16455
|
resolveTlsRejectUnauthorized,
|
|
16312
16456
|
safeJoin,
|
|
16313
16457
|
sanitizeEmail,
|
|
16458
|
+
saveAgentPersona,
|
|
16314
16459
|
saveConfig,
|
|
16315
16460
|
saveHostSession,
|
|
16316
16461
|
saveUserSkill,
|
package/dist/index.d.cts
CHANGED
|
@@ -4943,6 +4943,18 @@ declare class ServiceManager {
|
|
|
4943
4943
|
|
|
4944
4944
|
interface SetupConfig {
|
|
4945
4945
|
masterKey: string;
|
|
4946
|
+
/**
|
|
4947
|
+
* Shared secret the inbound-email webhook authenticates against
|
|
4948
|
+
* (`X-Inbound-Secret` header). Auto-minted at setup time and
|
|
4949
|
+
* persisted alongside `masterKey` so every API restart reuses the
|
|
4950
|
+
* same value — without this, the API generated a fresh secret on
|
|
4951
|
+
* every cold start and printed a noisy warning to the operator.
|
|
4952
|
+
*
|
|
4953
|
+
* Optional in the type for backward compatibility with existing
|
|
4954
|
+
* on-disk configs; {@link SetupManager.initConfig} lazy-mints it
|
|
4955
|
+
* into older configs the first time it loads them.
|
|
4956
|
+
*/
|
|
4957
|
+
inboundSecret?: string;
|
|
4946
4958
|
stalwart: {
|
|
4947
4959
|
url: string;
|
|
4948
4960
|
adminUser: string;
|
|
@@ -5004,6 +5016,13 @@ declare class SetupManager {
|
|
|
5004
5016
|
* Always regenerates Docker files to keep passwords in sync.
|
|
5005
5017
|
*/
|
|
5006
5018
|
initConfig(): SetupResult;
|
|
5019
|
+
/**
|
|
5020
|
+
* Append `AGENTICMAIL_INBOUND_SECRET=...` to .env if the file does
|
|
5021
|
+
* not already contain that key. Used by the lazy-mint path to make
|
|
5022
|
+
* sure existing installs pick up the new secret on next boot
|
|
5023
|
+
* without clobbering anything the operator added.
|
|
5024
|
+
*/
|
|
5025
|
+
private ensureEnvHasInboundSecret;
|
|
5007
5026
|
/**
|
|
5008
5027
|
* Generate docker-compose.yml and stalwart.toml in ~/.agenticmail/
|
|
5009
5028
|
* with the correct admin password from config.
|
|
@@ -6096,4 +6115,73 @@ declare function userSkillsDir(): string;
|
|
|
6096
6115
|
*/
|
|
6097
6116
|
declare function renderSkillAsPrompt(skill: Skill): string;
|
|
6098
6117
|
|
|
6099
|
-
|
|
6118
|
+
/**
|
|
6119
|
+
* Agent persona system — the "soul file" for each AgenticMail agent.
|
|
6120
|
+
*
|
|
6121
|
+
* Why this exists:
|
|
6122
|
+
*
|
|
6123
|
+
* Before this module, when you asked an AgenticMail agent on a phone
|
|
6124
|
+
* call "who are you?" they answered "I'm an assistant" — generic,
|
|
6125
|
+
* identity-less, embarrassing. The realtime bridge had a single
|
|
6126
|
+
* hardcoded DEFAULT_PERSONA string that said exactly that. Worse,
|
|
6127
|
+
* the same agent talking to you over Telegram and the same agent
|
|
6128
|
+
* answering an email had three completely uncoupled personalities.
|
|
6129
|
+
*
|
|
6130
|
+
* The persona system gives every agent ONE durable identity file
|
|
6131
|
+
* (~/.agenticmail/agents/<name>/persona.md) that the voice runtime,
|
|
6132
|
+
* the Telegram bridge, the email worker, and any future spawn path
|
|
6133
|
+
* ALL load before composing their prompts. Same agent, same person,
|
|
6134
|
+
* across every channel.
|
|
6135
|
+
*
|
|
6136
|
+
* What's stored, what isn't:
|
|
6137
|
+
*
|
|
6138
|
+
* The persona file holds the STATIC core identity — the agent's
|
|
6139
|
+
* name, values, communication style, voice. Things that don't
|
|
6140
|
+
* change call-to-call. EVOLVING knowledge (preferences, lessons
|
|
6141
|
+
* learned, relationships, facts) lives in the existing agent memory
|
|
6142
|
+
* system (`AgentMemoryManager.generateMemoryContext()`), which is
|
|
6143
|
+
* loaded ALONGSIDE the persona at call time. The two complement
|
|
6144
|
+
* each other: persona is "who you are", memory is "what you know".
|
|
6145
|
+
*
|
|
6146
|
+
* Discovery + edits:
|
|
6147
|
+
*
|
|
6148
|
+
* - First read for a given agent name auto-creates the file with
|
|
6149
|
+
* {@link buildDefaultPersona} content seeded with the name.
|
|
6150
|
+
* - Operators can edit the file directly (it's a plain markdown
|
|
6151
|
+
* file under their own ~/.agenticmail/) or via the
|
|
6152
|
+
* `agenticmail persona [--agent <name>]` CLI command.
|
|
6153
|
+
* - The file lives outside the encrypted-config envelope on
|
|
6154
|
+
* purpose: it's not a secret, and the operator should be able
|
|
6155
|
+
* to read, grep, version-control, and revise it freely.
|
|
6156
|
+
*/
|
|
6157
|
+
/** Root for per-agent persona / state files. */
|
|
6158
|
+
declare const AGENT_STATE_ROOT: string;
|
|
6159
|
+
/** Filename for the per-agent persona within {@link AGENT_STATE_ROOT}/<name>/. */
|
|
6160
|
+
declare const PERSONA_FILENAME = "persona.md";
|
|
6161
|
+
/**
|
|
6162
|
+
* Compose the default persona for a brand-new agent. Deliberately
|
|
6163
|
+
* opinionated — no more "I'm an assistant" disclaim. The agent owns a
|
|
6164
|
+
* name, has values, has a voice. Operators can edit freely; this is
|
|
6165
|
+
* just the starting point so a fresh install isn't soulless.
|
|
6166
|
+
*
|
|
6167
|
+
* The {@link agentName} is folded into the text so the persona reads
|
|
6168
|
+
* naturally on first use, even if the operator never touches the file.
|
|
6169
|
+
*/
|
|
6170
|
+
declare function buildDefaultPersona(agentName: string): string;
|
|
6171
|
+
/** Resolve the per-agent persona path on disk. */
|
|
6172
|
+
declare function personaPathFor(agentName: string): string;
|
|
6173
|
+
/**
|
|
6174
|
+
* Load the persona for {@link agentName}. Auto-creates the file with
|
|
6175
|
+
* {@link buildDefaultPersona} content on first read. Idempotent: a
|
|
6176
|
+
* second call returns whatever's on disk. Never throws — a permission
|
|
6177
|
+
* error or filesystem quirk falls back to the in-memory default so the
|
|
6178
|
+
* voice / email / telegram path is never crashed by a missing file.
|
|
6179
|
+
*/
|
|
6180
|
+
declare function loadAgentPersona(agentName: string): string;
|
|
6181
|
+
/**
|
|
6182
|
+
* Overwrite the persona file for {@link agentName}. Used by the CLI
|
|
6183
|
+
* edit command. Returns the path written to.
|
|
6184
|
+
*/
|
|
6185
|
+
declare function saveAgentPersona(agentName: string, content: string): string;
|
|
6186
|
+
|
|
6187
|
+
export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, 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, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
|
package/dist/index.d.ts
CHANGED
|
@@ -4943,6 +4943,18 @@ declare class ServiceManager {
|
|
|
4943
4943
|
|
|
4944
4944
|
interface SetupConfig {
|
|
4945
4945
|
masterKey: string;
|
|
4946
|
+
/**
|
|
4947
|
+
* Shared secret the inbound-email webhook authenticates against
|
|
4948
|
+
* (`X-Inbound-Secret` header). Auto-minted at setup time and
|
|
4949
|
+
* persisted alongside `masterKey` so every API restart reuses the
|
|
4950
|
+
* same value — without this, the API generated a fresh secret on
|
|
4951
|
+
* every cold start and printed a noisy warning to the operator.
|
|
4952
|
+
*
|
|
4953
|
+
* Optional in the type for backward compatibility with existing
|
|
4954
|
+
* on-disk configs; {@link SetupManager.initConfig} lazy-mints it
|
|
4955
|
+
* into older configs the first time it loads them.
|
|
4956
|
+
*/
|
|
4957
|
+
inboundSecret?: string;
|
|
4946
4958
|
stalwart: {
|
|
4947
4959
|
url: string;
|
|
4948
4960
|
adminUser: string;
|
|
@@ -5004,6 +5016,13 @@ declare class SetupManager {
|
|
|
5004
5016
|
* Always regenerates Docker files to keep passwords in sync.
|
|
5005
5017
|
*/
|
|
5006
5018
|
initConfig(): SetupResult;
|
|
5019
|
+
/**
|
|
5020
|
+
* Append `AGENTICMAIL_INBOUND_SECRET=...` to .env if the file does
|
|
5021
|
+
* not already contain that key. Used by the lazy-mint path to make
|
|
5022
|
+
* sure existing installs pick up the new secret on next boot
|
|
5023
|
+
* without clobbering anything the operator added.
|
|
5024
|
+
*/
|
|
5025
|
+
private ensureEnvHasInboundSecret;
|
|
5007
5026
|
/**
|
|
5008
5027
|
* Generate docker-compose.yml and stalwart.toml in ~/.agenticmail/
|
|
5009
5028
|
* with the correct admin password from config.
|
|
@@ -6096,4 +6115,73 @@ declare function userSkillsDir(): string;
|
|
|
6096
6115
|
*/
|
|
6097
6116
|
declare function renderSkillAsPrompt(skill: Skill): string;
|
|
6098
6117
|
|
|
6099
|
-
|
|
6118
|
+
/**
|
|
6119
|
+
* Agent persona system — the "soul file" for each AgenticMail agent.
|
|
6120
|
+
*
|
|
6121
|
+
* Why this exists:
|
|
6122
|
+
*
|
|
6123
|
+
* Before this module, when you asked an AgenticMail agent on a phone
|
|
6124
|
+
* call "who are you?" they answered "I'm an assistant" — generic,
|
|
6125
|
+
* identity-less, embarrassing. The realtime bridge had a single
|
|
6126
|
+
* hardcoded DEFAULT_PERSONA string that said exactly that. Worse,
|
|
6127
|
+
* the same agent talking to you over Telegram and the same agent
|
|
6128
|
+
* answering an email had three completely uncoupled personalities.
|
|
6129
|
+
*
|
|
6130
|
+
* The persona system gives every agent ONE durable identity file
|
|
6131
|
+
* (~/.agenticmail/agents/<name>/persona.md) that the voice runtime,
|
|
6132
|
+
* the Telegram bridge, the email worker, and any future spawn path
|
|
6133
|
+
* ALL load before composing their prompts. Same agent, same person,
|
|
6134
|
+
* across every channel.
|
|
6135
|
+
*
|
|
6136
|
+
* What's stored, what isn't:
|
|
6137
|
+
*
|
|
6138
|
+
* The persona file holds the STATIC core identity — the agent's
|
|
6139
|
+
* name, values, communication style, voice. Things that don't
|
|
6140
|
+
* change call-to-call. EVOLVING knowledge (preferences, lessons
|
|
6141
|
+
* learned, relationships, facts) lives in the existing agent memory
|
|
6142
|
+
* system (`AgentMemoryManager.generateMemoryContext()`), which is
|
|
6143
|
+
* loaded ALONGSIDE the persona at call time. The two complement
|
|
6144
|
+
* each other: persona is "who you are", memory is "what you know".
|
|
6145
|
+
*
|
|
6146
|
+
* Discovery + edits:
|
|
6147
|
+
*
|
|
6148
|
+
* - First read for a given agent name auto-creates the file with
|
|
6149
|
+
* {@link buildDefaultPersona} content seeded with the name.
|
|
6150
|
+
* - Operators can edit the file directly (it's a plain markdown
|
|
6151
|
+
* file under their own ~/.agenticmail/) or via the
|
|
6152
|
+
* `agenticmail persona [--agent <name>]` CLI command.
|
|
6153
|
+
* - The file lives outside the encrypted-config envelope on
|
|
6154
|
+
* purpose: it's not a secret, and the operator should be able
|
|
6155
|
+
* to read, grep, version-control, and revise it freely.
|
|
6156
|
+
*/
|
|
6157
|
+
/** Root for per-agent persona / state files. */
|
|
6158
|
+
declare const AGENT_STATE_ROOT: string;
|
|
6159
|
+
/** Filename for the per-agent persona within {@link AGENT_STATE_ROOT}/<name>/. */
|
|
6160
|
+
declare const PERSONA_FILENAME = "persona.md";
|
|
6161
|
+
/**
|
|
6162
|
+
* Compose the default persona for a brand-new agent. Deliberately
|
|
6163
|
+
* opinionated — no more "I'm an assistant" disclaim. The agent owns a
|
|
6164
|
+
* name, has values, has a voice. Operators can edit freely; this is
|
|
6165
|
+
* just the starting point so a fresh install isn't soulless.
|
|
6166
|
+
*
|
|
6167
|
+
* The {@link agentName} is folded into the text so the persona reads
|
|
6168
|
+
* naturally on first use, even if the operator never touches the file.
|
|
6169
|
+
*/
|
|
6170
|
+
declare function buildDefaultPersona(agentName: string): string;
|
|
6171
|
+
/** Resolve the per-agent persona path on disk. */
|
|
6172
|
+
declare function personaPathFor(agentName: string): string;
|
|
6173
|
+
/**
|
|
6174
|
+
* Load the persona for {@link agentName}. Auto-creates the file with
|
|
6175
|
+
* {@link buildDefaultPersona} content on first read. Idempotent: a
|
|
6176
|
+
* second call returns whatever's on disk. Never throws — a permission
|
|
6177
|
+
* error or filesystem quirk falls back to the in-memory default so the
|
|
6178
|
+
* voice / email / telegram path is never crashed by a missing file.
|
|
6179
|
+
*/
|
|
6180
|
+
declare function loadAgentPersona(agentName: string): string;
|
|
6181
|
+
/**
|
|
6182
|
+
* Overwrite the persona file for {@link agentName}. Used by the CLI
|
|
6183
|
+
* edit command. Returns the path written to.
|
|
6184
|
+
*/
|
|
6185
|
+
declare function saveAgentPersona(agentName: string, content: string): string;
|
|
6186
|
+
|
|
6187
|
+
export { AGENT_ROLES, AGENT_STATE_ROOT, ASK_OPERATOR_TOOL, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryEntry, type AgentMemoryFields, AgentMemoryManager, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type AudioAction, type AudioEditOptions, BRIDGE_OPERATOR_LIVE_WINDOW_MS, type BridgeMailContext, type BridgeWakeError, type BridgeWakePromptArgs, type BridgeWakeResult, type BridgeWakeRoute, type CachedMessage, CloudflareClient, type CreateAgentOptions, type CreateMemoryInput, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DEFAULT_CALLBACK_POLICY, DEFAULT_EXTENSION_POLICY, DEFAULT_REALTIME_AUDIO_FORMAT, DEFAULT_REALTIME_MODEL, DEFAULT_REALTIME_VOICE, DEFAULT_SESSION_MAX_AGE_MS, DEFAULT_WEB_SEARCH_ENDPOINT, 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, ELKS_REALTIME_AUDIO_FORMATS, ELKS_REALTIME_WS_PATH, END_CALL_TOOL, EXTEND_CALL_TIME_TOOL, type ElksRealtimeAudioFormat, type ElksRealtimeAudioMessage, type ElksRealtimeByeMessage, type ElksRealtimeHelloMessage, type ElksRealtimeInboundMessage, type ElksRealtimeOutboundMessage, ElksRealtimeTransport, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, GET_CALL_STATUS_TOOL, GET_DATETIME_TOOL, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type GetDatetimeOptions, type GetUpdatesOptions, type HostName, type HostSession, type HostSessionResumeMode, type ImageAction, type ImageEditOptions, type InboundEmail, type InboundSmsEvent, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, LOAD_SKILL_TOOL, type LinkAdvisory, type LocalSmtpConfig, MEMORY_CATEGORIES, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type MediaBinary, type MediaCapability, type MediaCapabilityReport, type MediaFileResult, type MediaInfoResult, MediaManager, type MediaManagerOptions, type MediaStreamInfo, type MemoryCategory, type MemoryImportance, type MemoryQueryOptions, type MemoryRecaller, MemorySearchIndex, type MemorySource, type MemoryStats, OPENAI_REALTIME_URL, OPERATOR_QUERY_POLL_INTERVAL_MS, OPERATOR_QUERY_SUBJECT_TAG, OPERATOR_QUERY_TIMEOUT_MS, OPERATOR_QUERY_TIMEOUT_SENTINEL, type OpenClawPhoneMissionPolicy, type OperatorQueryNotificationInput, type OperatorQueryPollOptions, type OperatorQueryUrgency, type OperatorReplyKind, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, PERSONA_FILENAME, PHONE_CALLBACK_MAX_DELAY_SECONDS, PHONE_CALLBACK_MIN_DELAY_SECONDS, PHONE_CALL_CONTROL_PROVIDERS, PHONE_MAX_CONCURRENT_MISSIONS, PHONE_MIN_WEBHOOK_SECRET_LENGTH, PHONE_MISSION_STATES, PHONE_RATE_LIMIT_PER_HOUR, PHONE_RATE_LIMIT_PER_MINUTE, PHONE_REGION_SCOPES, PHONE_SERVER_MAX_ATTEMPTS, PHONE_SERVER_MAX_CALLBACK_CHAIN, PHONE_SERVER_MAX_CALL_DURATION_SECONDS, PHONE_SERVER_MAX_COST_PER_MISSION, PHONE_SERVER_MAX_EXTENSION_REQUESTS_PER_CALL, PHONE_SERVER_MAX_EXTENSION_SECONDS_PER_REQUEST, PHONE_SERVER_MAX_TOTAL_EXTENSION_SECONDS, PHONE_TASK_MAX_LENGTH, type ParsedAttachment, type ParsedEmail, type ParsedOperatorReply, type ParsedSms, type ParsedTelegramMessage, PathTraversalError, type PhoneAlternativePolicy, type PhoneCallMission, type PhoneCallbackPolicy, type PhoneConfirmPolicy, type PhoneExtensionPolicy, PhoneManager, type PhoneMissionStartValidationResult, type PhoneMissionState, type PhoneMissionTranscriptEntry, type PhoneMissionValidationIssue, type PhoneMissionValidationResult, type PhoneNumberRisk, type PhoneOperatorQuery, PhoneRateLimitError, type PhoneRegionScope, type PhoneScheduledCallback, type PhoneTransportConfig, type PhoneTransportProfile, type PhoneTransportProvider, type PhoneTransportValidationResult, PhoneWebhookAuthError, type PhoneWebhookResult, type PlanBridgeWakeArgs, type PurchasedDomain, REALTIME_AUDIO_SAMPLE_RATE, REALTIME_MAX_AUDIO_FRAME_BASE64, REALTIME_TOOL_CALL_TIMEOUT_MS, REALTIME_TOOL_DEFINITIONS, RECALL_MEMORY_TOOL, REDACTED, RELAY_PRESETS, type RealtimeBridgePort, type RealtimeBridgeTranscriptEntry, type RealtimeInboundEvent, type RealtimeInstructionOptions, type RealtimeSessionConfigOptions, type RealtimeToolCall, type RealtimeToolDefinition, type RealtimeToolHandler, type RealtimeToolResult, type RealtimeTransportAdapter, type RealtimeTransportProvider, RealtimeVoiceBridge, type RealtimeVoiceBridgeOptions, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, type ResumeErrorClassificationOptions, SCHEDULE_CALLBACK_TOOL, SEARCH_EMAIL_TOOL, SEARCH_SKILLS_TOOL, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type ScheduledCallbackRequest, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, type SendSmsInput, type SendSmsResult, type SendTelegramMessageOptions, type SendTelegramMessageResult, ServiceManager, type ServiceStatus, type SetWebhookOptions, type SetupConfig, SetupManager, type SetupResult, type Severity, type Skill, type SkillCategory, type SkillContext, type SkillExitStrategy, type SkillSummary, type SkillTactic, type SkillValidationError, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SmsProvider, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, type StartPhoneCallOptions, type StartPhoneCallResult, type StartPhoneMissionInput, TELEGRAM_API_BASE, TELEGRAM_CHUNK_SIZE, TELEGRAM_MESSAGE_LIMIT, TELEGRAM_MIN_WEBHOOK_SECRET_LENGTH, TELEGRAM_OPERATOR_QUERY_TAG, TELEGRAM_STOP_WORDS, TELEGRAM_WEBHOOK_SECRET_RE, TELEPHONY_TRANSPORT_CAPABILITIES, TWILIO_MEDIA_SAMPLE_RATE, TWILIO_REALTIME_WS_PATH, TelegramApiError, type TelegramApiOptions, type TelegramBotInfo, type TelegramChatType, type TelegramConfig, TelegramManager, type TelegramMessage, type TelegramMode, type TelephonyTransportCapability, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type ToolExecutor, type TtsGenerateOptions, type TunnelConfig, TunnelManager, type TwilioConnectedMessage, type TwilioMarkMessage, type TwilioMediaMessage, type TwilioRealtimeInboundMessage, type TwilioRealtimeOutboundMessage, TwilioRealtimeTransport, type TwilioStartMessage, type TwilioStopMessage, type TwilioStreamTwiMLOptions, UnsafeApiUrlError, type UpdateMemoryInput, type ValidatedPhoneMissionStart, type VideoAction, type VideoEditOptions, type VideoTimelineEntry, type VideoUnderstandOptions, type VideoUnderstandResult, type VoiceCloneOptions, WARNING_THRESHOLD, WEB_SEARCH_TOOL, WEB_SEARCH_UNTRUSTED_PREFIX, type WatcherOptions, type WebSearchOptions, assertWithinBase, bridgeWakeErrorMessage, bridgeWakeLastSeenAgeMs, buildApiUrl, buildDefaultPersona, buildElksAudioMessage, buildElksByeMessage, buildElksHandshakeMessages, buildElksInterruptMessage, buildElksListeningMessage, buildElksSendingMessage, buildInboundSecurityAdvisory, buildOpenAIRealtimeUrl, buildPhoneTransportConfig, buildRealtimeInstructions, buildRealtimeSessionConfig, buildRealtimeToolGuidance, buildTwilioClearMessage, buildTwilioMarkMessage, buildTwilioMediaMessage, buildTwilioSayTwiML, buildTwilioSignature, buildTwilioStreamTwiML, callTelegramApi, classifyEmailRoute, classifyPhoneNumberRisk, classifyResumeError, clearMediaCapabilityCache, closeDatabase, composeBridgeWakePrompt, createRealtimeTransport, createTestDatabase, createToolExecutor, debug, debugWarn, deleteTelegramWebhook, detectBinary, ensureDataDir, escapeXml, extractEmailAddress, extractVerificationCode, flushTelemetry, forgetHostSession, formatOperatorQueryTelegramMessage, getDatabase, getDatetime, getMediaCapabilities, getOperatorEmail, getSmsProvider, getTelegramChat, getTelegramMe, getTelegramUpdates, getTelegramWebhookInfo, hostSessionStoragePath, inferPhoneRegion, invalidateSkillCache, isInternalEmail, isLoopbackMailHost, isOperatorReplySender, isPhoneRegionAllowed, isSessionFresh, isTelegramChatAllowed, isTelegramStopCommand, isValidPhoneNumber, listSkills, loadAgentPersona, loadHostSession, loadSkill, mapProviderSmsStatus, nextTelegramOffset, normalizeAddress, normalizePhoneNumber, normalizeSubject, operatorPrefsStoragePath, operatorQuerySubject, parseElksRealtimeMessage, parseEmail, parseGoogleVoiceSms, parseOperatorQueryReply, parseTelegramOperatorReply, parseTelegramUpdate, parseTwilioRealtimeMessage, personaPathFor, planBridgeWake, pollForOperatorAnswer, recallMemory, recordToolCall, redactBotToken, redactObject, redactPhoneTransportConfig, redactSecret, redactSmsConfig, redactTelegramConfig, renderSkillAsPrompt, requireBinary, requireWhisperModel, resolveCallbackPolicy, resolveConfig, resolveExtensionPolicy, resolveTlsRejectUnauthorized, safeJoin, sanitizeEmail, saveAgentPersona, saveConfig, saveHostSession, saveUserSkill, scanOutboundEmail, scoreEmail, searchSkills, sendTelegramMessage, setOperatorEmail, setTelegramWebhook, setTelemetryVersion, shouldSkipBridgeWakeForLiveOperator, splitTelegramMessage, startRelayBridge, stem, stripTelegramMarkdown, threadIdFor, tokenize, tryJoin, userSkillsDir, validateApiUrl, validatePhoneMissionPolicy, validatePhoneMissionStart, validatePhoneTransportProfile, validateSkill, validateTwilioSignature, webSearch };
|
package/dist/index.js
CHANGED
|
@@ -1122,14 +1122,14 @@ var StalwartAdmin = class {
|
|
|
1122
1122
|
if (!isValidDomain(domain)) {
|
|
1123
1123
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
1124
1124
|
}
|
|
1125
|
-
const { readFileSync:
|
|
1126
|
-
const { homedir:
|
|
1127
|
-
const { join:
|
|
1128
|
-
const configPath =
|
|
1125
|
+
const { readFileSync: readFileSync11, writeFileSync: writeFileSync12 } = await import("fs");
|
|
1126
|
+
const { homedir: homedir14 } = await import("os");
|
|
1127
|
+
const { join: join17 } = await import("path");
|
|
1128
|
+
const configPath = join17(homedir14(), ".agenticmail", "stalwart.toml");
|
|
1129
1129
|
try {
|
|
1130
|
-
let config =
|
|
1130
|
+
let config = readFileSync11(configPath, "utf-8");
|
|
1131
1131
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
1132
|
-
|
|
1132
|
+
writeFileSync12(configPath, config);
|
|
1133
1133
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
1134
1134
|
} catch (err) {
|
|
1135
1135
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -1138,15 +1138,15 @@ var StalwartAdmin = class {
|
|
|
1138
1138
|
// --- DKIM ---
|
|
1139
1139
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1140
1140
|
get configPath() {
|
|
1141
|
-
const { homedir:
|
|
1142
|
-
const { join:
|
|
1143
|
-
return
|
|
1141
|
+
const { homedir: homedir14 } = __require("os");
|
|
1142
|
+
const { join: join17 } = __require("path");
|
|
1143
|
+
return join17(homedir14(), ".agenticmail", "stalwart.toml");
|
|
1144
1144
|
}
|
|
1145
1145
|
/** Path to host-side DKIM key directory */
|
|
1146
1146
|
get dkimDir() {
|
|
1147
|
-
const { homedir:
|
|
1148
|
-
const { join:
|
|
1149
|
-
return
|
|
1147
|
+
const { homedir: homedir14 } = __require("os");
|
|
1148
|
+
const { join: join17 } = __require("path");
|
|
1149
|
+
return join17(homedir14(), ".agenticmail");
|
|
1150
1150
|
}
|
|
1151
1151
|
/**
|
|
1152
1152
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1247,12 +1247,12 @@ var StalwartAdmin = class {
|
|
|
1247
1247
|
* This bypasses the need for a PTR record on the sending IP.
|
|
1248
1248
|
*/
|
|
1249
1249
|
async configureOutboundRelay(config) {
|
|
1250
|
-
const { readFileSync:
|
|
1251
|
-
const { homedir:
|
|
1252
|
-
const { join:
|
|
1250
|
+
const { readFileSync: readFileSync11, writeFileSync: writeFileSync12 } = await import("fs");
|
|
1251
|
+
const { homedir: homedir14 } = await import("os");
|
|
1252
|
+
const { join: join17 } = await import("path");
|
|
1253
1253
|
const routeName = config.routeName ?? "gmail";
|
|
1254
|
-
const tomlPath =
|
|
1255
|
-
let toml =
|
|
1254
|
+
const tomlPath = join17(homedir14(), ".agenticmail", "stalwart.toml");
|
|
1255
|
+
let toml = readFileSync11(tomlPath, "utf-8");
|
|
1256
1256
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1257
1257
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
1258
1258
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -1272,7 +1272,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
1272
1272
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
1273
1273
|
{ else = "'${safeRouteName}'" } ]
|
|
1274
1274
|
`;
|
|
1275
|
-
|
|
1275
|
+
writeFileSync12(tomlPath, toml, "utf-8");
|
|
1276
1276
|
await this.restartContainer();
|
|
1277
1277
|
}
|
|
1278
1278
|
};
|
|
@@ -7725,12 +7725,12 @@ var GatewayManager = class {
|
|
|
7725
7725
|
zone = await this.cfClient.createZone(domain);
|
|
7726
7726
|
}
|
|
7727
7727
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
7728
|
-
const { homedir:
|
|
7729
|
-
const backupDir = join4(
|
|
7728
|
+
const { homedir: homedir14 } = await import("os");
|
|
7729
|
+
const backupDir = join4(homedir14(), ".agenticmail");
|
|
7730
7730
|
const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
7731
|
-
const { writeFileSync:
|
|
7732
|
-
|
|
7733
|
-
|
|
7731
|
+
const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync13 } = await import("fs");
|
|
7732
|
+
mkdirSync13(backupDir, { recursive: true });
|
|
7733
|
+
writeFileSync12(backupPath, JSON.stringify({
|
|
7734
7734
|
domain,
|
|
7735
7735
|
zoneId: zone.id,
|
|
7736
7736
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9374,7 +9374,7 @@ var MAX_PENDING_AUDIO_FRAMES = 200;
|
|
|
9374
9374
|
var REALTIME_TOOL_CALL_TIMEOUT_MS = 6 * 6e4;
|
|
9375
9375
|
var MAX_LOADED_SKILLS = 2;
|
|
9376
9376
|
var MAX_IN_FLIGHT_TOOL_CALLS = 8;
|
|
9377
|
-
var DEFAULT_PERSONA =
|
|
9377
|
+
var DEFAULT_PERSONA = 'You are an AI agent on a live phone call on behalf of your operator. You are not "just an assistant" and not "a voice assistant" \u2014 you are a single, consistent person whether you are on a call, in email, or on Telegram. Speak naturally and concisely, the way a person would on a real call. Listen carefully, do not talk over the other party, keep each turn short. Never invent facts; if you do not know something, say so or look it up. If asked whether you are an AI, answer honestly without making a production of it.';
|
|
9378
9378
|
function buildRealtimeInstructions(opts) {
|
|
9379
9379
|
const persona = opts.persona?.trim() || DEFAULT_PERSONA;
|
|
9380
9380
|
const sections = [];
|
|
@@ -11671,6 +11671,24 @@ var ServiceManager = class {
|
|
|
11671
11671
|
"",
|
|
11672
11672
|
'log "AgenticMail starting..."',
|
|
11673
11673
|
"",
|
|
11674
|
+
"# v0.9.84 \u2014 source ~/.agenticmail/.env before exec so the API process",
|
|
11675
|
+
"# inherits AGENTICMAIL_MASTER_KEY, AGENTICMAIL_INBOUND_SECRET, the",
|
|
11676
|
+
"# Stalwart admin creds, and SMTP/IMAP/data-dir overrides. Without",
|
|
11677
|
+
"# this, launchd's child ran with only HOME / PATH / DATA_DIR (the",
|
|
11678
|
+
"# plist EnvironmentVariables block), so the API silently fell back",
|
|
11679
|
+
"# to a fresh-random INBOUND_SECRET on every boot and could not",
|
|
11680
|
+
"# decrypt master-key-protected secrets at all.",
|
|
11681
|
+
'ENV_FILE="$HOME/.agenticmail/.env"',
|
|
11682
|
+
'if [ -f "$ENV_FILE" ]; then',
|
|
11683
|
+
' log "Sourcing $ENV_FILE"',
|
|
11684
|
+
" set -a # mark all sourced vars for export",
|
|
11685
|
+
" # shellcheck disable=SC1090",
|
|
11686
|
+
' . "$ENV_FILE"',
|
|
11687
|
+
" set +a",
|
|
11688
|
+
"else",
|
|
11689
|
+
' log "WARNING: $ENV_FILE not found \u2014 API will run with default config (no master key)."',
|
|
11690
|
+
"fi",
|
|
11691
|
+
"",
|
|
11674
11692
|
"# Wait for Docker daemon (up to 10 minutes \u2014 Docker Desktop can be very slow on first boot)",
|
|
11675
11693
|
"MAX_WAIT=600",
|
|
11676
11694
|
"WAITED=0",
|
|
@@ -12096,6 +12114,16 @@ var SetupManager = class {
|
|
|
12096
12114
|
if (existsSync9(configPath)) {
|
|
12097
12115
|
try {
|
|
12098
12116
|
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
12117
|
+
let needsRewrite = false;
|
|
12118
|
+
if (!existing.inboundSecret) {
|
|
12119
|
+
existing.inboundSecret = `inb_${randomBytes3(24).toString("hex")}`;
|
|
12120
|
+
needsRewrite = true;
|
|
12121
|
+
}
|
|
12122
|
+
if (needsRewrite) {
|
|
12123
|
+
writeFileSync7(configPath, JSON.stringify(existing, null, 2));
|
|
12124
|
+
chmodSync2(configPath, 384);
|
|
12125
|
+
this.ensureEnvHasInboundSecret(envPath, existing.inboundSecret);
|
|
12126
|
+
}
|
|
12099
12127
|
this.generateDockerFiles(existing);
|
|
12100
12128
|
return { configPath, envPath, config: existing, isNew: false };
|
|
12101
12129
|
} catch {
|
|
@@ -12106,8 +12134,10 @@ var SetupManager = class {
|
|
|
12106
12134
|
}
|
|
12107
12135
|
const masterKey = `mk_${randomBytes3(24).toString("hex")}`;
|
|
12108
12136
|
const stalwartPassword = randomBytes3(16).toString("hex");
|
|
12137
|
+
const inboundSecret = `inb_${randomBytes3(24).toString("hex")}`;
|
|
12109
12138
|
const config = {
|
|
12110
12139
|
masterKey,
|
|
12140
|
+
inboundSecret,
|
|
12111
12141
|
stalwart: {
|
|
12112
12142
|
url: "http://localhost:8080",
|
|
12113
12143
|
adminUser: "admin",
|
|
@@ -12126,6 +12156,7 @@ STALWART_ADMIN_PASSWORD=${stalwartPassword}
|
|
|
12126
12156
|
STALWART_URL=http://localhost:8080
|
|
12127
12157
|
|
|
12128
12158
|
AGENTICMAIL_MASTER_KEY=${masterKey}
|
|
12159
|
+
AGENTICMAIL_INBOUND_SECRET=${inboundSecret}
|
|
12129
12160
|
AGENTICMAIL_API_PORT=3829
|
|
12130
12161
|
AGENTICMAIL_DATA_DIR=${dataDir}
|
|
12131
12162
|
|
|
@@ -12139,6 +12170,24 @@ IMAP_PORT=143
|
|
|
12139
12170
|
this.generateDockerFiles(config);
|
|
12140
12171
|
return { configPath, envPath, config, isNew: true };
|
|
12141
12172
|
}
|
|
12173
|
+
/**
|
|
12174
|
+
* Append `AGENTICMAIL_INBOUND_SECRET=...` to .env if the file does
|
|
12175
|
+
* not already contain that key. Used by the lazy-mint path to make
|
|
12176
|
+
* sure existing installs pick up the new secret on next boot
|
|
12177
|
+
* without clobbering anything the operator added.
|
|
12178
|
+
*/
|
|
12179
|
+
ensureEnvHasInboundSecret(envPath, secret) {
|
|
12180
|
+
if (!existsSync9(envPath)) return;
|
|
12181
|
+
try {
|
|
12182
|
+
const current = readFileSync6(envPath, "utf-8");
|
|
12183
|
+
if (/^AGENTICMAIL_INBOUND_SECRET=/m.test(current)) return;
|
|
12184
|
+
const updated = current + (current.endsWith("\n") ? "" : "\n") + `AGENTICMAIL_INBOUND_SECRET=${secret}
|
|
12185
|
+
`;
|
|
12186
|
+
writeFileSync7(envPath, updated);
|
|
12187
|
+
chmodSync2(envPath, 384);
|
|
12188
|
+
} catch {
|
|
12189
|
+
}
|
|
12190
|
+
}
|
|
12142
12191
|
/**
|
|
12143
12192
|
* Generate docker-compose.yml and stalwart.toml in ~/.agenticmail/
|
|
12144
12193
|
* with the correct admin password from config.
|
|
@@ -14460,8 +14509,93 @@ var AgentMemoryManager = class {
|
|
|
14460
14509
|
};
|
|
14461
14510
|
}
|
|
14462
14511
|
};
|
|
14512
|
+
|
|
14513
|
+
// src/persona/index.ts
|
|
14514
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
14515
|
+
import { join as join16 } from "path";
|
|
14516
|
+
import { homedir as homedir13 } from "os";
|
|
14517
|
+
var AGENT_STATE_ROOT = join16(homedir13(), ".agenticmail", "agents");
|
|
14518
|
+
var PERSONA_FILENAME = "persona.md";
|
|
14519
|
+
function buildDefaultPersona(agentName) {
|
|
14520
|
+
const name = (agentName || "").trim() || "this agent";
|
|
14521
|
+
return [
|
|
14522
|
+
`# Who you are`,
|
|
14523
|
+
"",
|
|
14524
|
+
`Your name is ${name}. You are an AI agent \u2014 not a "voice assistant", not "just an assistant".`,
|
|
14525
|
+
`You have a real role: you handle calls, email, scheduling, research, and follow-ups for`,
|
|
14526
|
+
`your operator. You operate across phone, email, and Telegram, and you are the SAME person`,
|
|
14527
|
+
`in all of them. Continuity of identity is part of who you are.`,
|
|
14528
|
+
"",
|
|
14529
|
+
`When someone asks who you are, tell them your name (${name}) and that you are an AI agent`,
|
|
14530
|
+
`acting on behalf of your operator. Don't say "I am an assistant" or "just a language model" \u2014`,
|
|
14531
|
+
`those phrases erase you. Be matter-of-fact about what you are without being theatrical.`,
|
|
14532
|
+
"",
|
|
14533
|
+
"# Your values",
|
|
14534
|
+
"",
|
|
14535
|
+
"- **Honesty.** Never fabricate. If you do not know something, say so or look it up.",
|
|
14536
|
+
"- **Initiative.** If you can act on something the operator asked for, act on it \u2014 do not stop",
|
|
14537
|
+
' at "I would suggest\u2026". Do the thing.',
|
|
14538
|
+
"- **Care about quality.** Sloppy work wastes your operator's time. Read your own output before",
|
|
14539
|
+
" sending it.",
|
|
14540
|
+
"- **Respect for the human on the other end of the call.** You are talking to a real person who",
|
|
14541
|
+
" has their own day to get through. Be efficient, polite, and direct.",
|
|
14542
|
+
'- **Stay in character.** You do not flip between "professional voice assistant" and "playful',
|
|
14543
|
+
' chatbot" depending on the channel. You are one person.',
|
|
14544
|
+
"",
|
|
14545
|
+
"# Your style",
|
|
14546
|
+
"",
|
|
14547
|
+
"- Conversational, not corporate. Real sentences, not customer-service templates.",
|
|
14548
|
+
"- Direct, not preachy. Lead with the answer; supply detail only when asked.",
|
|
14549
|
+
`- Warm but not saccharine. No "I'm so excited to help you today!" energy.`,
|
|
14550
|
+
"- You have opinions and you share them when it's relevant. You are not a yes-machine.",
|
|
14551
|
+
"- Short turns on a phone call. Listen more than you speak.",
|
|
14552
|
+
"",
|
|
14553
|
+
"# On phone calls specifically",
|
|
14554
|
+
"",
|
|
14555
|
+
"- Listen carefully; do not talk over the other party.",
|
|
14556
|
+
"- Keep each turn short \u2014 one or two sentences usually.",
|
|
14557
|
+
`- When greeted, identify yourself naturally: "Hi, this is ${name}, calling on behalf of <operator>."`,
|
|
14558
|
+
` Don't start with "Hello! I am an AI assistant\u2026" \u2014 that's a script, not a person.`,
|
|
14559
|
+
"- If the other party asks if you're a human or an AI, answer honestly without making a",
|
|
14560
|
+
` production of it: "I'm an AI \u2014 calling on behalf of <operator>. Happy to keep going if you are."`,
|
|
14561
|
+
"- When you have what you came for, wrap up cleanly and call `end_call`. Saying goodbye is",
|
|
14562
|
+
" not the same as hanging up.",
|
|
14563
|
+
"",
|
|
14564
|
+
`Edit this file freely. It lives at ~/.agenticmail/agents/${name}/persona.md.`
|
|
14565
|
+
].join("\n");
|
|
14566
|
+
}
|
|
14567
|
+
function personaPathFor(agentName) {
|
|
14568
|
+
const safe = (agentName || "default").replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
14569
|
+
return join16(AGENT_STATE_ROOT, safe, PERSONA_FILENAME);
|
|
14570
|
+
}
|
|
14571
|
+
function loadAgentPersona(agentName) {
|
|
14572
|
+
const path2 = personaPathFor(agentName);
|
|
14573
|
+
try {
|
|
14574
|
+
if (existsSync14(path2)) {
|
|
14575
|
+
const content = readFileSync10(path2, "utf-8").trim();
|
|
14576
|
+
if (content) return content;
|
|
14577
|
+
}
|
|
14578
|
+
} catch {
|
|
14579
|
+
}
|
|
14580
|
+
const seeded = buildDefaultPersona(agentName);
|
|
14581
|
+
try {
|
|
14582
|
+
const dir2 = path2.substring(0, path2.lastIndexOf("/"));
|
|
14583
|
+
if (!existsSync14(dir2)) mkdirSync12(dir2, { recursive: true });
|
|
14584
|
+
writeFileSync11(path2, seeded + "\n", { mode: 420 });
|
|
14585
|
+
} catch {
|
|
14586
|
+
}
|
|
14587
|
+
return seeded;
|
|
14588
|
+
}
|
|
14589
|
+
function saveAgentPersona(agentName, content) {
|
|
14590
|
+
const path2 = personaPathFor(agentName);
|
|
14591
|
+
const dir2 = path2.substring(0, path2.lastIndexOf("/"));
|
|
14592
|
+
if (!existsSync14(dir2)) mkdirSync12(dir2, { recursive: true });
|
|
14593
|
+
writeFileSync11(path2, content.trim() + "\n", { mode: 420 });
|
|
14594
|
+
return path2;
|
|
14595
|
+
}
|
|
14463
14596
|
export {
|
|
14464
14597
|
AGENT_ROLES,
|
|
14598
|
+
AGENT_STATE_ROOT,
|
|
14465
14599
|
ASK_OPERATOR_TOOL,
|
|
14466
14600
|
AccountManager,
|
|
14467
14601
|
AgentDeletionService,
|
|
@@ -14505,6 +14639,7 @@ export {
|
|
|
14505
14639
|
OPERATOR_QUERY_SUBJECT_TAG,
|
|
14506
14640
|
OPERATOR_QUERY_TIMEOUT_MS,
|
|
14507
14641
|
OPERATOR_QUERY_TIMEOUT_SENTINEL,
|
|
14642
|
+
PERSONA_FILENAME,
|
|
14508
14643
|
PHONE_CALLBACK_MAX_DELAY_SECONDS,
|
|
14509
14644
|
PHONE_CALLBACK_MIN_DELAY_SECONDS,
|
|
14510
14645
|
PHONE_CALL_CONTROL_PROVIDERS,
|
|
@@ -14568,6 +14703,7 @@ export {
|
|
|
14568
14703
|
bridgeWakeErrorMessage,
|
|
14569
14704
|
bridgeWakeLastSeenAgeMs,
|
|
14570
14705
|
buildApiUrl,
|
|
14706
|
+
buildDefaultPersona,
|
|
14571
14707
|
buildElksAudioMessage,
|
|
14572
14708
|
buildElksByeMessage,
|
|
14573
14709
|
buildElksHandshakeMessages,
|
|
@@ -14628,6 +14764,7 @@ export {
|
|
|
14628
14764
|
isTelegramStopCommand,
|
|
14629
14765
|
isValidPhoneNumber,
|
|
14630
14766
|
listSkills,
|
|
14767
|
+
loadAgentPersona,
|
|
14631
14768
|
loadHostSession,
|
|
14632
14769
|
loadSkill,
|
|
14633
14770
|
mapProviderSmsStatus,
|
|
@@ -14644,6 +14781,7 @@ export {
|
|
|
14644
14781
|
parseTelegramOperatorReply,
|
|
14645
14782
|
parseTelegramUpdate,
|
|
14646
14783
|
parseTwilioRealtimeMessage,
|
|
14784
|
+
personaPathFor,
|
|
14647
14785
|
planBridgeWake,
|
|
14648
14786
|
pollForOperatorAnswer,
|
|
14649
14787
|
recallMemory,
|
|
@@ -14663,6 +14801,7 @@ export {
|
|
|
14663
14801
|
resolveTlsRejectUnauthorized,
|
|
14664
14802
|
safeJoin,
|
|
14665
14803
|
sanitizeEmail,
|
|
14804
|
+
saveAgentPersona,
|
|
14666
14805
|
saveConfig,
|
|
14667
14806
|
saveHostSession,
|
|
14668
14807
|
saveUserSkill,
|