@agenticmail/core 0.9.23 → 0.9.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -851,6 +851,7 @@ __export(index_exports, {
851
851
  getTelegramWebhookInfo: () => getTelegramWebhookInfo,
852
852
  hostSessionStoragePath: () => hostSessionStoragePath,
853
853
  inferPhoneRegion: () => inferPhoneRegion,
854
+ invalidateSkillCache: () => invalidateSkillCache,
854
855
  isInternalEmail: () => isInternalEmail,
855
856
  isLoopbackMailHost: () => isLoopbackMailHost,
856
857
  isOperatorReplySender: () => isOperatorReplySender,
@@ -859,7 +860,9 @@ __export(index_exports, {
859
860
  isTelegramChatAllowed: () => isTelegramChatAllowed,
860
861
  isTelegramStopCommand: () => isTelegramStopCommand,
861
862
  isValidPhoneNumber: () => isValidPhoneNumber,
863
+ listSkills: () => listSkills,
862
864
  loadHostSession: () => loadHostSession,
865
+ loadSkill: () => loadSkill,
863
866
  mapProviderSmsStatus: () => mapProviderSmsStatus,
864
867
  nextTelegramOffset: () => nextTelegramOffset,
865
868
  normalizeAddress: () => normalizeAddress,
@@ -884,6 +887,7 @@ __export(index_exports, {
884
887
  redactSecret: () => redactSecret,
885
888
  redactSmsConfig: () => redactSmsConfig,
886
889
  redactTelegramConfig: () => redactTelegramConfig,
890
+ renderSkillAsPrompt: () => renderSkillAsPrompt,
887
891
  requireBinary: () => requireBinary,
888
892
  requireWhisperModel: () => requireWhisperModel,
889
893
  resolveConfig: () => resolveConfig,
@@ -892,8 +896,10 @@ __export(index_exports, {
892
896
  sanitizeEmail: () => sanitizeEmail,
893
897
  saveConfig: () => saveConfig,
894
898
  saveHostSession: () => saveHostSession,
899
+ saveUserSkill: () => saveUserSkill,
895
900
  scanOutboundEmail: () => scanOutboundEmail,
896
901
  scoreEmail: () => scoreEmail,
902
+ searchSkills: () => searchSkills,
897
903
  sendTelegramMessage: () => sendTelegramMessage,
898
904
  setOperatorEmail: () => setOperatorEmail,
899
905
  setTelegramWebhook: () => setTelegramWebhook,
@@ -906,10 +912,12 @@ __export(index_exports, {
906
912
  threadIdFor: () => threadIdFor,
907
913
  tokenize: () => tokenize,
908
914
  tryJoin: () => tryJoin,
915
+ userSkillsDir: () => userSkillsDir,
909
916
  validateApiUrl: () => validateApiUrl,
910
917
  validatePhoneMissionPolicy: () => validatePhoneMissionPolicy,
911
918
  validatePhoneMissionStart: () => validatePhoneMissionStart,
912
919
  validatePhoneTransportProfile: () => validatePhoneTransportProfile,
920
+ validateSkill: () => validateSkill,
913
921
  validateTwilioSignature: () => validateTwilioSignature,
914
922
  webSearch: () => webSearch
915
923
  });
@@ -2016,14 +2024,14 @@ var StalwartAdmin = class {
2016
2024
  if (!isValidDomain(domain)) {
2017
2025
  throw new Error(`Invalid domain format: "${domain}"`);
2018
2026
  }
2019
- const { readFileSync: readFileSync10, writeFileSync: writeFileSync11 } = await import("fs");
2020
- const { homedir: homedir13 } = await import("os");
2021
- const { join: join16 } = await import("path");
2022
- const configPath = join16(homedir13(), ".agenticmail", "stalwart.toml");
2027
+ const { readFileSync: readFileSync11, writeFileSync: writeFileSync12 } = await import("fs");
2028
+ const { homedir: homedir14 } = await import("os");
2029
+ const { join: join17 } = await import("path");
2030
+ const configPath = join17(homedir14(), ".agenticmail", "stalwart.toml");
2023
2031
  try {
2024
- let config = readFileSync10(configPath, "utf-8");
2032
+ let config = readFileSync11(configPath, "utf-8");
2025
2033
  config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
2026
- writeFileSync11(configPath, config);
2034
+ writeFileSync12(configPath, config);
2027
2035
  console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
2028
2036
  } catch (err) {
2029
2037
  throw new Error(`Failed to set config server.hostname=${domain}`);
@@ -2032,15 +2040,15 @@ var StalwartAdmin = class {
2032
2040
  // --- DKIM ---
2033
2041
  /** Path to the host-side stalwart.toml (mounted read-only into container) */
2034
2042
  get configPath() {
2035
- const { homedir: homedir13 } = require("os");
2036
- const { join: join16 } = require("path");
2037
- return join16(homedir13(), ".agenticmail", "stalwart.toml");
2043
+ const { homedir: homedir14 } = require("os");
2044
+ const { join: join17 } = require("path");
2045
+ return join17(homedir14(), ".agenticmail", "stalwart.toml");
2038
2046
  }
2039
2047
  /** Path to host-side DKIM key directory */
2040
2048
  get dkimDir() {
2041
- const { homedir: homedir13 } = require("os");
2042
- const { join: join16 } = require("path");
2043
- return join16(homedir13(), ".agenticmail");
2049
+ const { homedir: homedir14 } = require("os");
2050
+ const { join: join17 } = require("path");
2051
+ return join17(homedir14(), ".agenticmail");
2044
2052
  }
2045
2053
  /**
2046
2054
  * Create/reuse a DKIM signing key for a domain.
@@ -2141,12 +2149,12 @@ var StalwartAdmin = class {
2141
2149
  * This bypasses the need for a PTR record on the sending IP.
2142
2150
  */
2143
2151
  async configureOutboundRelay(config) {
2144
- const { readFileSync: readFileSync10, writeFileSync: writeFileSync11 } = await import("fs");
2145
- const { homedir: homedir13 } = await import("os");
2146
- const { join: join16 } = await import("path");
2152
+ const { readFileSync: readFileSync11, writeFileSync: writeFileSync12 } = await import("fs");
2153
+ const { homedir: homedir14 } = await import("os");
2154
+ const { join: join17 } = await import("path");
2147
2155
  const routeName = config.routeName ?? "gmail";
2148
- const tomlPath = join16(homedir13(), ".agenticmail", "stalwart.toml");
2149
- let toml = readFileSync10(tomlPath, "utf-8");
2156
+ const tomlPath = join17(homedir14(), ".agenticmail", "stalwart.toml");
2157
+ let toml = readFileSync11(tomlPath, "utf-8");
2150
2158
  toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
2151
2159
  toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
2152
2160
  const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
@@ -2166,7 +2174,7 @@ auth.secret = "${escapeTomlString(config.password)}"
2166
2174
  route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
2167
2175
  { else = "'${safeRouteName}'" } ]
2168
2176
  `;
2169
- writeFileSync11(tomlPath, toml, "utf-8");
2177
+ writeFileSync12(tomlPath, toml, "utf-8");
2170
2178
  await this.restartContainer();
2171
2179
  }
2172
2180
  };
@@ -8372,12 +8380,12 @@ var GatewayManager = class {
8372
8380
  zone = await this.cfClient.createZone(domain);
8373
8381
  }
8374
8382
  const existingRecords = await this.cfClient.listDnsRecords(zone.id);
8375
- const { homedir: homedir13 } = await import("os");
8376
- const backupDir = (0, import_node_path4.join)(homedir13(), ".agenticmail");
8383
+ const { homedir: homedir14 } = await import("os");
8384
+ const backupDir = (0, import_node_path4.join)(homedir14(), ".agenticmail");
8377
8385
  const backupPath = (0, import_node_path4.join)(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
8378
- const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync12 } = await import("fs");
8379
- mkdirSync12(backupDir, { recursive: true });
8380
- writeFileSync11(backupPath, JSON.stringify({
8386
+ const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync13 } = await import("fs");
8387
+ mkdirSync13(backupDir, { recursive: true });
8388
+ writeFileSync12(backupPath, JSON.stringify({
8381
8389
  domain,
8382
8390
  zoneId: zone.id,
8383
8391
  backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -14763,6 +14771,285 @@ var AgentMemoryManager = class {
14763
14771
  };
14764
14772
  }
14765
14773
  };
14774
+
14775
+ // src/skills/registry.ts
14776
+ var import_node_fs14 = require("fs");
14777
+ var import_node_path15 = require("path");
14778
+ var import_node_url = require("url");
14779
+ var import_node_os12 = require("os");
14780
+ var import_meta4 = {};
14781
+ function builtInDir() {
14782
+ const here = (0, import_node_path15.dirname)((0, import_node_url.fileURLToPath)(import_meta4.url));
14783
+ return (0, import_node_path15.join)(here, "built-in");
14784
+ }
14785
+ function userDir() {
14786
+ const dir2 = (0, import_node_path15.join)((0, import_node_os12.homedir)(), ".agenticmail", "skills");
14787
+ try {
14788
+ if (!(0, import_node_fs14.existsSync)(dir2)) (0, import_node_fs14.mkdirSync)(dir2, { recursive: true });
14789
+ } catch {
14790
+ }
14791
+ return dir2;
14792
+ }
14793
+ var cache = { ts: 0, byId: null };
14794
+ var CACHE_TTL_MS = 5e3;
14795
+ function loadAllSkillsFromDisk() {
14796
+ const all = /* @__PURE__ */ new Map();
14797
+ const dirs = [builtInDir(), userDir()];
14798
+ for (const dir2 of dirs) {
14799
+ if (!(0, import_node_fs14.existsSync)(dir2)) continue;
14800
+ let entries;
14801
+ try {
14802
+ entries = (0, import_node_fs14.readdirSync)(dir2);
14803
+ } catch {
14804
+ continue;
14805
+ }
14806
+ for (const entry of entries) {
14807
+ if (!entry.endsWith(".json")) continue;
14808
+ const fullPath = (0, import_node_path15.join)(dir2, entry);
14809
+ try {
14810
+ const st = (0, import_node_fs14.statSync)(fullPath);
14811
+ if (!st.isFile()) continue;
14812
+ const raw = (0, import_node_fs14.readFileSync)(fullPath, "utf-8");
14813
+ const parsed = JSON.parse(raw);
14814
+ const errors = validateSkill(parsed);
14815
+ if (errors.length > 0) {
14816
+ console.warn(`[skills] ${entry} invalid, skipping: ${errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
14817
+ continue;
14818
+ }
14819
+ all.set(parsed.id, parsed);
14820
+ } catch (err) {
14821
+ console.warn(`[skills] could not load ${fullPath}: ${err.message}`);
14822
+ }
14823
+ }
14824
+ }
14825
+ return all;
14826
+ }
14827
+ function ensureLoaded() {
14828
+ const now = Date.now();
14829
+ if (cache.byId && now - cache.ts < CACHE_TTL_MS) return cache.byId;
14830
+ const fresh = loadAllSkillsFromDisk();
14831
+ cache.byId = fresh;
14832
+ cache.ts = now;
14833
+ return fresh;
14834
+ }
14835
+ function invalidateSkillCache() {
14836
+ cache.byId = null;
14837
+ cache.ts = 0;
14838
+ }
14839
+ function validateSkill(s) {
14840
+ const errs = [];
14841
+ if (!s || typeof s !== "object" || Array.isArray(s)) {
14842
+ return [{ path: "$", message: "skill must be a JSON object" }];
14843
+ }
14844
+ const sk = s;
14845
+ const requireString = (key, minLen = 1) => {
14846
+ const v = sk[key];
14847
+ if (typeof v !== "string" || v.length < minLen) {
14848
+ errs.push({ path: key, message: `must be a non-empty string` });
14849
+ }
14850
+ };
14851
+ const requireArray = (key, minLen = 1) => {
14852
+ const v = sk[key];
14853
+ if (!Array.isArray(v) || v.length < minLen) {
14854
+ errs.push({ path: key, message: `must be a non-empty array` });
14855
+ }
14856
+ };
14857
+ requireString("id");
14858
+ if (typeof sk.id === "string" && !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(sk.id)) {
14859
+ errs.push({ path: "id", message: "must be lowercase-hyphenated slug (a-z, 0-9, -)" });
14860
+ }
14861
+ requireString("name");
14862
+ requireString("version");
14863
+ requireString("description");
14864
+ requireString("category");
14865
+ const validCategories = [
14866
+ "negotiation",
14867
+ "customer-service",
14868
+ "reservations",
14869
+ "medical-admin",
14870
+ "legal-admin",
14871
+ "finance-admin",
14872
+ "real-estate",
14873
+ "travel",
14874
+ "subscription",
14875
+ "home-services",
14876
+ "social",
14877
+ "civic",
14878
+ "employment",
14879
+ "debt-collection",
14880
+ "other"
14881
+ ];
14882
+ if (typeof sk.category === "string" && !validCategories.includes(sk.category)) {
14883
+ errs.push({ path: "category", message: `unknown category "${sk.category}"; must be one of: ${validCategories.join(", ")}` });
14884
+ }
14885
+ if (!Array.isArray(sk.tags)) errs.push({ path: "tags", message: "must be an array of strings" });
14886
+ if (sk.disclaimer !== null && typeof sk.disclaimer !== "string") {
14887
+ errs.push({ path: "disclaimer", message: "must be a string or null" });
14888
+ }
14889
+ if (!sk.context || typeof sk.context !== "object") {
14890
+ errs.push({ path: "context", message: "must be an object" });
14891
+ } else {
14892
+ const ctx = sk.context;
14893
+ if (typeof ctx.when_to_use !== "string") errs.push({ path: "context.when_to_use", message: "must be a string" });
14894
+ if (!Array.isArray(ctx.preconditions)) errs.push({ path: "context.preconditions", message: "must be an array" });
14895
+ if (typeof ctx.estimated_call_duration_minutes !== "number") errs.push({ path: "context.estimated_call_duration_minutes", message: "must be a number" });
14896
+ }
14897
+ requireArray("principles", 2);
14898
+ if (!sk.phrases || typeof sk.phrases !== "object") errs.push({ path: "phrases", message: "must be an object of {key: phrase}" });
14899
+ if (!Array.isArray(sk.tactics) || sk.tactics.length === 0) {
14900
+ errs.push({ path: "tactics", message: "must be a non-empty array" });
14901
+ } else {
14902
+ sk.tactics.forEach((t, i) => {
14903
+ if (!t || typeof t !== "object") {
14904
+ errs.push({ path: `tactics[${i}]`, message: "must be an object" });
14905
+ return;
14906
+ }
14907
+ const tactic = t;
14908
+ if (typeof tactic.name !== "string") errs.push({ path: `tactics[${i}].name`, message: "must be a string" });
14909
+ if (typeof tactic.when !== "string") errs.push({ path: `tactics[${i}].when`, message: "must be a string" });
14910
+ if (typeof tactic.script !== "string" || tactic.script.length < 5) {
14911
+ errs.push({ path: `tactics[${i}].script`, message: "must be a non-empty string (>= 5 chars)" });
14912
+ }
14913
+ });
14914
+ }
14915
+ requireArray("boundaries", 1);
14916
+ if (!Array.isArray(sk.success_signals)) errs.push({ path: "success_signals", message: "must be an array" });
14917
+ if (!Array.isArray(sk.failure_signals)) errs.push({ path: "failure_signals", message: "must be an array" });
14918
+ if (!sk.exit_strategy || typeof sk.exit_strategy !== "object") {
14919
+ errs.push({ path: "exit_strategy", message: "must be an object" });
14920
+ } else {
14921
+ const xs = sk.exit_strategy;
14922
+ if (typeof xs.on_success !== "string") errs.push({ path: "exit_strategy.on_success", message: "must be a string" });
14923
+ if (typeof xs.on_failure !== "string") errs.push({ path: "exit_strategy.on_failure", message: "must be a string" });
14924
+ }
14925
+ if (!Array.isArray(sk.required_user_info)) errs.push({ path: "required_user_info", message: "must be an array" });
14926
+ if (typeof sk.contributed_by !== "string") errs.push({ path: "contributed_by", message: "must be a string" });
14927
+ return errs;
14928
+ }
14929
+ function summarize(s) {
14930
+ return {
14931
+ id: s.id,
14932
+ name: s.name,
14933
+ category: s.category,
14934
+ tags: s.tags,
14935
+ description: s.description,
14936
+ version: s.version,
14937
+ disclaimer_required: !!s.disclaimer,
14938
+ estimated_call_duration_minutes: s.context.estimated_call_duration_minutes
14939
+ };
14940
+ }
14941
+ function listSkills(opts = {}) {
14942
+ const all = Array.from(ensureLoaded().values());
14943
+ const filtered = all.filter((s) => {
14944
+ if (opts.category && s.category !== opts.category) return false;
14945
+ if (opts.tag && !s.tags.includes(opts.tag.toLowerCase())) return false;
14946
+ return true;
14947
+ });
14948
+ return filtered.sort((a, b) => a.name.localeCompare(b.name)).map(summarize);
14949
+ }
14950
+ function searchSkills(query, limit = 20) {
14951
+ const q = query.toLowerCase().trim();
14952
+ if (!q) return listSkills();
14953
+ const all = Array.from(ensureLoaded().values());
14954
+ const scored = [];
14955
+ for (const s of all) {
14956
+ let score = 0;
14957
+ if (s.id.toLowerCase().includes(q)) score += 10;
14958
+ if (s.name.toLowerCase().includes(q)) score += 8;
14959
+ if (s.tags.some((t) => t.toLowerCase().includes(q))) score += 5;
14960
+ if (s.category.toLowerCase().includes(q)) score += 4;
14961
+ if (s.description.toLowerCase().includes(q)) score += 3;
14962
+ if (s.principles.some((p) => p.toLowerCase().includes(q))) score += 2;
14963
+ if (Object.values(s.phrases).some((p) => p.toLowerCase().includes(q))) score += 2;
14964
+ if (s.tactics.some((t) => t.script.toLowerCase().includes(q) || t.name.toLowerCase().includes(q))) score += 2;
14965
+ if (score > 0) scored.push({ skill: s, score });
14966
+ }
14967
+ scored.sort((a, b) => b.score - a.score || a.skill.name.localeCompare(b.skill.name));
14968
+ return scored.slice(0, limit).map((x) => summarize(x.skill));
14969
+ }
14970
+ function loadSkill(id) {
14971
+ return ensureLoaded().get(id) ?? null;
14972
+ }
14973
+ function saveUserSkill(skill) {
14974
+ const errors = validateSkill(skill);
14975
+ if (errors.length > 0) {
14976
+ throw new Error(`skill validation failed: ${errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
14977
+ }
14978
+ const dir2 = userDir();
14979
+ const path2 = (0, import_node_path15.join)(dir2, `${skill.id}.json`);
14980
+ const now = (/* @__PURE__ */ new Date()).toISOString();
14981
+ const out = {
14982
+ ...skill,
14983
+ created_at: skill.created_at ?? now,
14984
+ updated_at: now
14985
+ };
14986
+ (0, import_node_fs14.writeFileSync)(path2, JSON.stringify(out, null, 2), "utf-8");
14987
+ invalidateSkillCache();
14988
+ return { path: path2 };
14989
+ }
14990
+ function userSkillsDir() {
14991
+ return userDir();
14992
+ }
14993
+
14994
+ // src/skills/index.ts
14995
+ function renderSkillAsPrompt(skill) {
14996
+ const lines = [];
14997
+ lines.push(`=== SKILL LOADED: ${skill.name} (v${skill.version}) ===`);
14998
+ lines.push(`Category: ${skill.category} Tags: ${skill.tags.join(", ")}`);
14999
+ lines.push("");
15000
+ lines.push(skill.description);
15001
+ lines.push("");
15002
+ if (skill.disclaimer) {
15003
+ lines.push("REQUIRED DISCLAIMER (recite at start of the substantive turn):");
15004
+ lines.push(` "${skill.disclaimer}"`);
15005
+ lines.push("");
15006
+ }
15007
+ lines.push("WHEN TO USE THIS:");
15008
+ lines.push(` ${skill.context.when_to_use}`);
15009
+ if (skill.context.preconditions.length > 0) {
15010
+ lines.push("Preconditions:");
15011
+ for (const p of skill.context.preconditions) lines.push(` - ${p}`);
15012
+ }
15013
+ lines.push("");
15014
+ lines.push("PRINCIPLES:");
15015
+ for (const p of skill.principles) lines.push(` - ${p}`);
15016
+ lines.push("");
15017
+ if (Object.keys(skill.phrases).length > 0) {
15018
+ lines.push("PHRASES (paraphrase to match your voice; keep the structural move):");
15019
+ for (const [k, v] of Object.entries(skill.phrases)) lines.push(` [${k}] "${v}"`);
15020
+ lines.push("");
15021
+ }
15022
+ if (skill.tactics.length > 0) {
15023
+ lines.push("TACTICS (try in order; fall back to next on failure):");
15024
+ skill.tactics.forEach((t, i) => {
15025
+ lines.push(` ${i + 1}. ${t.name}`);
15026
+ lines.push(` When: ${t.when}`);
15027
+ lines.push(` Script: "${t.script}"`);
15028
+ });
15029
+ lines.push("");
15030
+ }
15031
+ if (skill.boundaries.length > 0) {
15032
+ lines.push("HARD BOUNDARIES \u2014 do not cross:");
15033
+ for (const b of skill.boundaries) lines.push(` - ${b}`);
15034
+ lines.push("");
15035
+ }
15036
+ lines.push("SUCCESS SIGNALS:");
15037
+ for (const s of skill.success_signals) lines.push(` - ${s}`);
15038
+ lines.push("");
15039
+ lines.push("FAILURE SIGNALS \u2014 when these appear, escalate or exit:");
15040
+ for (const s of skill.failure_signals) lines.push(` - ${s}`);
15041
+ lines.push("");
15042
+ lines.push("EXIT:");
15043
+ lines.push(` On success: ${skill.exit_strategy.on_success}`);
15044
+ lines.push(` On failure: ${skill.exit_strategy.on_failure}`);
15045
+ if (skill.exit_strategy.follow_ups && skill.exit_strategy.follow_ups.length > 0) {
15046
+ lines.push(" Follow-ups (after the call):");
15047
+ for (const f of skill.exit_strategy.follow_ups) lines.push(` - ${f}`);
15048
+ }
15049
+ lines.push("");
15050
+ lines.push("=== END SKILL ===");
15051
+ return lines.join("\n");
15052
+ }
14766
15053
  // Annotate the CommonJS export names for ESM import in node:
14767
15054
  0 && (module.exports = {
14768
15055
  AGENT_ROLES,
@@ -14908,6 +15195,7 @@ var AgentMemoryManager = class {
14908
15195
  getTelegramWebhookInfo,
14909
15196
  hostSessionStoragePath,
14910
15197
  inferPhoneRegion,
15198
+ invalidateSkillCache,
14911
15199
  isInternalEmail,
14912
15200
  isLoopbackMailHost,
14913
15201
  isOperatorReplySender,
@@ -14916,7 +15204,9 @@ var AgentMemoryManager = class {
14916
15204
  isTelegramChatAllowed,
14917
15205
  isTelegramStopCommand,
14918
15206
  isValidPhoneNumber,
15207
+ listSkills,
14919
15208
  loadHostSession,
15209
+ loadSkill,
14920
15210
  mapProviderSmsStatus,
14921
15211
  nextTelegramOffset,
14922
15212
  normalizeAddress,
@@ -14941,6 +15231,7 @@ var AgentMemoryManager = class {
14941
15231
  redactSecret,
14942
15232
  redactSmsConfig,
14943
15233
  redactTelegramConfig,
15234
+ renderSkillAsPrompt,
14944
15235
  requireBinary,
14945
15236
  requireWhisperModel,
14946
15237
  resolveConfig,
@@ -14949,8 +15240,10 @@ var AgentMemoryManager = class {
14949
15240
  sanitizeEmail,
14950
15241
  saveConfig,
14951
15242
  saveHostSession,
15243
+ saveUserSkill,
14952
15244
  scanOutboundEmail,
14953
15245
  scoreEmail,
15246
+ searchSkills,
14954
15247
  sendTelegramMessage,
14955
15248
  setOperatorEmail,
14956
15249
  setTelegramWebhook,
@@ -14963,10 +15256,12 @@ var AgentMemoryManager = class {
14963
15256
  threadIdFor,
14964
15257
  tokenize,
14965
15258
  tryJoin,
15259
+ userSkillsDir,
14966
15260
  validateApiUrl,
14967
15261
  validatePhoneMissionPolicy,
14968
15262
  validatePhoneMissionStart,
14969
15263
  validatePhoneTransportProfile,
15264
+ validateSkill,
14970
15265
  validateTwilioSignature,
14971
15266
  webSearch
14972
15267
  });