@hasna/assistants 1.1.8 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +8376 -4671
  3. package/dist/lib.js +2043 -47
  4. package/package.json +2 -2
package/dist/lib.js CHANGED
@@ -110939,7 +110939,7 @@ await init_runtime();
110939
110939
 
110940
110940
  // ../core/src/agent/loop.ts
110941
110941
  init_src2();
110942
- import { join as join47 } from "path";
110942
+ import { join as join48 } from "path";
110943
110943
 
110944
110944
  // ../core/src/agent/context.ts
110945
110945
  init_src2();
@@ -124618,7 +124618,9 @@ class BuiltinCommands {
124618
124618
  loader.register(this.messagesCommand());
124619
124619
  loader.register(this.webhooksCommand());
124620
124620
  loader.register(this.channelsCommand());
124621
+ loader.register(this.phoneCommand());
124621
124622
  loader.register(this.tasksCommand());
124623
+ loader.register(this.setupCommand());
124622
124624
  loader.register(this.exitCommand());
124623
124625
  }
124624
124626
  voiceCommand() {
@@ -127904,6 +127906,208 @@ Configure the external source with the URL and secret above.
127904
127906
  context.emit("text", `Unknown command: ${subcommand}
127905
127907
  `);
127906
127908
  context.emit("text", `Use /channels help for available commands.
127909
+ `);
127910
+ context.emit("done");
127911
+ return { handled: true };
127912
+ }
127913
+ };
127914
+ }
127915
+ phoneCommand() {
127916
+ return {
127917
+ name: "phone",
127918
+ description: "Manage telephony: SMS, calls, WhatsApp, routing",
127919
+ builtin: true,
127920
+ selfHandled: true,
127921
+ content: "",
127922
+ handler: async (args, context) => {
127923
+ const trimmed = args.trim();
127924
+ const [subcommand, ...rest] = trimmed.split(/\s+/);
127925
+ const subArgs = rest.join(" ");
127926
+ if (!subcommand || subcommand === "ui") {
127927
+ context.emit("done");
127928
+ return { handled: true, showPanel: "telephony" };
127929
+ }
127930
+ const manager = context.getTelephonyManager?.();
127931
+ if (!manager) {
127932
+ context.emit("text", `Telephony is not enabled. Set telephony.enabled: true in config.
127933
+ `);
127934
+ context.emit("done");
127935
+ return { handled: true };
127936
+ }
127937
+ if (subcommand === "numbers") {
127938
+ const numbers = manager.listPhoneNumbers();
127939
+ if (numbers.length === 0) {
127940
+ context.emit("text", `No phone numbers configured. Use /phone sync to import from Twilio.
127941
+ `);
127942
+ } else {
127943
+ context.emit("text", `Phone Numbers (${numbers.length}):
127944
+
127945
+ `);
127946
+ for (const num of numbers) {
127947
+ const caps = [];
127948
+ if (num.capabilities.voice)
127949
+ caps.push("voice");
127950
+ if (num.capabilities.sms)
127951
+ caps.push("sms");
127952
+ if (num.capabilities.whatsapp)
127953
+ caps.push("whatsapp");
127954
+ const name = num.friendlyName ? ` (${num.friendlyName})` : "";
127955
+ context.emit("text", ` ${num.number}${name} [${caps.join(", ")}]
127956
+ `);
127957
+ }
127958
+ }
127959
+ context.emit("done");
127960
+ return { handled: true };
127961
+ }
127962
+ if (subcommand === "sync") {
127963
+ context.emit("text", `Syncing phone numbers from Twilio...
127964
+ `);
127965
+ const result = await manager.syncPhoneNumbers();
127966
+ context.emit("text", `${result.success ? result.message : `Error: ${result.message}`}
127967
+ `);
127968
+ context.emit("done");
127969
+ return { handled: true };
127970
+ }
127971
+ if (subcommand === "sms") {
127972
+ const smsParts = subArgs.trim().split(/\s+/);
127973
+ const smsAction = smsParts[0];
127974
+ if (smsAction === "send") {
127975
+ const to = smsParts[1];
127976
+ const body = smsParts.slice(2).join(" ");
127977
+ if (!to || !body) {
127978
+ context.emit("text", `Usage: /phone sms send <to> <body>
127979
+ `);
127980
+ context.emit("done");
127981
+ return { handled: true };
127982
+ }
127983
+ const result = await manager.sendSms(to, body);
127984
+ context.emit("text", `${result.success ? result.message : `Error: ${result.message}`}
127985
+ `);
127986
+ } else if (smsAction === "list" || !smsAction) {
127987
+ const messages = manager.getSmsHistory({ limit: 20 });
127988
+ if (messages.length === 0) {
127989
+ context.emit("text", `No SMS history.
127990
+ `);
127991
+ } else {
127992
+ context.emit("text", `Recent SMS (${messages.length}):
127993
+
127994
+ `);
127995
+ for (const msg of messages) {
127996
+ const dir = msg.direction === "inbound" ? "IN" : "OUT";
127997
+ context.emit("text", ` [${dir}] ${msg.fromNumber} \u2192 ${msg.toNumber}: ${msg.bodyPreview}
127998
+ `);
127999
+ }
128000
+ }
128001
+ } else {
128002
+ context.emit("text", `Usage: /phone sms [send <to> <body> | list]
128003
+ `);
128004
+ }
128005
+ context.emit("done");
128006
+ return { handled: true };
128007
+ }
128008
+ if (subcommand === "call") {
128009
+ const to = subArgs.trim();
128010
+ if (!to) {
128011
+ context.emit("text", `Usage: /phone call <to>
128012
+ `);
128013
+ context.emit("done");
128014
+ return { handled: true };
128015
+ }
128016
+ const result = await manager.makeCall(to);
128017
+ context.emit("text", `${result.success ? result.message : `Error: ${result.message}`}
128018
+ `);
128019
+ context.emit("done");
128020
+ return { handled: true };
128021
+ }
128022
+ if (subcommand === "calls") {
128023
+ const calls = manager.getCallHistory({ limit: 20 });
128024
+ if (calls.length === 0) {
128025
+ context.emit("text", `No call history.
128026
+ `);
128027
+ } else {
128028
+ context.emit("text", `Recent Calls (${calls.length}):
128029
+
128030
+ `);
128031
+ for (const call of calls) {
128032
+ const dir = call.direction === "inbound" ? "IN" : "OUT";
128033
+ const dur = call.duration != null ? `${call.duration}s` : "-";
128034
+ context.emit("text", ` [${dir}] ${call.fromNumber} \u2192 ${call.toNumber} | ${call.status} | ${dur}
128035
+ `);
128036
+ }
128037
+ }
128038
+ context.emit("done");
128039
+ return { handled: true };
128040
+ }
128041
+ if (subcommand === "routes") {
128042
+ const rules = manager.listRoutingRules();
128043
+ if (rules.length === 0) {
128044
+ context.emit("text", `No routing rules configured.
128045
+ `);
128046
+ } else {
128047
+ context.emit("text", `Routing Rules (${rules.length}):
128048
+
128049
+ `);
128050
+ for (const rule of rules) {
128051
+ const enabled = rule.enabled ? "" : " [DISABLED]";
128052
+ context.emit("text", ` ${rule.name} (priority: ${rule.priority})${enabled}
128053
+ `);
128054
+ context.emit("text", ` Target: ${rule.targetAssistantName} | Type: ${rule.messageType}
128055
+ `);
128056
+ }
128057
+ }
128058
+ context.emit("done");
128059
+ return { handled: true };
128060
+ }
128061
+ if (subcommand === "status") {
128062
+ const status = manager.getStatus();
128063
+ context.emit("text", `Telephony Status:
128064
+
128065
+ `);
128066
+ context.emit("text", ` Enabled: ${status.enabled ? "Yes" : "No"}
128067
+ `);
128068
+ context.emit("text", ` Twilio: ${status.twilioConfigured ? "Configured" : "Not configured"}
128069
+ `);
128070
+ context.emit("text", ` ElevenLabs: ${status.elevenLabsConfigured ? "Configured" : "Not configured"}
128071
+ `);
128072
+ context.emit("text", ` Numbers: ${status.phoneNumbers}
128073
+ `);
128074
+ context.emit("text", ` Active calls: ${status.activeCalls}
128075
+ `);
128076
+ context.emit("text", ` Routes: ${status.routingRules}
128077
+ `);
128078
+ context.emit("done");
128079
+ return { handled: true };
128080
+ }
128081
+ if (subcommand === "help") {
128082
+ context.emit("text", `Phone Commands:
128083
+
128084
+ `);
128085
+ context.emit("text", `/phone Open telephony panel
128086
+ `);
128087
+ context.emit("text", `/phone numbers List phone numbers
128088
+ `);
128089
+ context.emit("text", `/phone sync Sync numbers from Twilio
128090
+ `);
128091
+ context.emit("text", `/phone sms send <to> <body> Send SMS
128092
+ `);
128093
+ context.emit("text", `/phone sms list Recent SMS
128094
+ `);
128095
+ context.emit("text", `/phone call <to> Initiate call
128096
+ `);
128097
+ context.emit("text", `/phone calls Recent calls
128098
+ `);
128099
+ context.emit("text", `/phone routes Routing rules
128100
+ `);
128101
+ context.emit("text", `/phone status Status summary
128102
+ `);
128103
+ context.emit("text", `/phone help Show this help
128104
+ `);
128105
+ context.emit("done");
128106
+ return { handled: true };
128107
+ }
128108
+ context.emit("text", `Unknown command: ${subcommand}
128109
+ `);
128110
+ context.emit("text", `Use /phone help for available commands.
127907
128111
  `);
127908
128112
  context.emit("done");
127909
128113
  return { handled: true };
@@ -128391,6 +128595,20 @@ Unknown workspace command. Use /workspace help for available commands.
128391
128595
  }
128392
128596
  };
128393
128597
  }
128598
+ setupCommand() {
128599
+ return {
128600
+ name: "setup",
128601
+ aliases: ["onboarding"],
128602
+ description: "Run the interactive setup wizard",
128603
+ builtin: true,
128604
+ selfHandled: true,
128605
+ content: "",
128606
+ handler: async (_args, context) => {
128607
+ context.emit("done");
128608
+ return { handled: true, showPanel: "setup" };
128609
+ }
128610
+ };
128611
+ }
128394
128612
  exitCommand() {
128395
128613
  return {
128396
128614
  name: "exit",
@@ -150618,6 +150836,7 @@ class ChannelStore {
150618
150836
  role TEXT NOT NULL DEFAULT 'member',
150619
150837
  joined_at TEXT NOT NULL,
150620
150838
  last_read_at TEXT,
150839
+ member_type TEXT NOT NULL DEFAULT 'assistant',
150621
150840
  PRIMARY KEY (channel_id, assistant_id)
150622
150841
  );
150623
150842
 
@@ -150635,6 +150854,16 @@ class ChannelStore {
150635
150854
  CREATE INDEX IF NOT EXISTS idx_channel_members_assistant
150636
150855
  ON channel_members(assistant_id);
150637
150856
  `);
150857
+ this.migrateAddMemberType();
150858
+ }
150859
+ migrateAddMemberType() {
150860
+ try {
150861
+ const columns = this.db.prepare("PRAGMA table_info(channel_members)").all();
150862
+ const hasMemberType = columns.some((col) => String(col.name) === "member_type");
150863
+ if (!hasMemberType) {
150864
+ this.db.exec("ALTER TABLE channel_members ADD COLUMN member_type TEXT NOT NULL DEFAULT 'assistant'");
150865
+ }
150866
+ } catch {}
150638
150867
  }
150639
150868
  createChannel(name2, description, createdBy, createdByName) {
150640
150869
  const normalizedName = name2.toLowerCase().replace(/^#/, "").replace(/[^a-z0-9_-]/g, "-");
@@ -150729,12 +150958,12 @@ class ChannelStore {
150729
150958
  const result = stmt.run("archived", now2, id, "active");
150730
150959
  return result.changes > 0;
150731
150960
  }
150732
- addMember(channelId, assistantId, assistantName, role = "member") {
150961
+ addMember(channelId, assistantId, assistantName, role = "member", memberType = "assistant") {
150733
150962
  try {
150734
150963
  const now2 = new Date().toISOString();
150735
- const stmt = this.db.prepare(`INSERT OR IGNORE INTO channel_members (channel_id, assistant_id, assistant_name, role, joined_at)
150736
- VALUES (?, ?, ?, ?, ?)`);
150737
- const result = stmt.run(channelId, assistantId, assistantName, role, now2);
150964
+ const stmt = this.db.prepare(`INSERT OR IGNORE INTO channel_members (channel_id, assistant_id, assistant_name, role, joined_at, member_type)
150965
+ VALUES (?, ?, ?, ?, ?, ?)`);
150966
+ const result = stmt.run(channelId, assistantId, assistantName, role, now2, memberType);
150738
150967
  return result.changes > 0;
150739
150968
  } catch {
150740
150969
  return false;
@@ -150874,7 +151103,8 @@ class ChannelStore {
150874
151103
  assistantName: String(row.assistant_name),
150875
151104
  role: String(row.role),
150876
151105
  joinedAt: String(row.joined_at),
150877
- lastReadAt: row.last_read_at ? String(row.last_read_at) : null
151106
+ lastReadAt: row.last_read_at ? String(row.last_read_at) : null,
151107
+ memberType: row.member_type ? String(row.member_type) : "assistant"
150878
151108
  };
150879
151109
  }
150880
151110
  rowToMessage(row) {
@@ -150952,7 +151182,7 @@ class ChannelsManager {
150952
151182
  this.store.removeMember(channel.id, this.assistantId);
150953
151183
  return { success: true, message: `Left #${channel.name}.`, channelId: channel.id };
150954
151184
  }
150955
- invite(nameOrId, targetId, targetName) {
151185
+ invite(nameOrId, targetId, targetName, memberType = "assistant") {
150956
151186
  const channel = this.store.resolveChannel(nameOrId);
150957
151187
  if (!channel) {
150958
151188
  return { success: false, message: `Channel "${nameOrId}" not found.` };
@@ -150963,7 +151193,7 @@ class ChannelsManager {
150963
151193
  if (this.store.isMember(channel.id, targetId)) {
150964
151194
  return { success: false, message: `${targetName} is already a member of #${channel.name}.` };
150965
151195
  }
150966
- this.store.addMember(channel.id, targetId, targetName);
151196
+ this.store.addMember(channel.id, targetId, targetName, "member", memberType);
150967
151197
  return {
150968
151198
  success: true,
150969
151199
  message: `Invited ${targetName} to #${channel.name}.`,
@@ -150994,6 +151224,24 @@ class ChannelsManager {
150994
151224
  channelId: channel.id
150995
151225
  };
150996
151226
  }
151227
+ sendAs(nameOrId, content, senderId, senderName) {
151228
+ const channel = this.store.resolveChannel(nameOrId);
151229
+ if (!channel) {
151230
+ return { success: false, message: `Channel "${nameOrId}" not found.` };
151231
+ }
151232
+ if (channel.status !== "active") {
151233
+ return { success: false, message: `Channel #${channel.name} is archived.` };
151234
+ }
151235
+ if (!this.store.isMember(channel.id, senderId)) {
151236
+ return { success: false, message: `${senderName} is not a member of #${channel.name}. Join first.` };
151237
+ }
151238
+ const messageId = this.store.sendMessage(channel.id, senderId, senderName, content);
151239
+ return {
151240
+ success: true,
151241
+ message: `Message sent to #${channel.name} (${messageId}).`,
151242
+ channelId: channel.id
151243
+ };
151244
+ }
150997
151245
  readMessages(nameOrId, limit2) {
150998
151246
  const channel = this.store.resolveChannel(nameOrId);
150999
151247
  if (!channel)
@@ -151361,25 +151609,1728 @@ function registerChannelTools(registry2, getChannelsManager) {
151361
151609
  registry2.register(tool, executors[tool.name]);
151362
151610
  }
151363
151611
  }
151612
+ // ../core/src/telephony/store.ts
151613
+ init_src2();
151614
+ await __promiseAll([
151615
+ init_config(),
151616
+ init_runtime()
151617
+ ]);
151618
+ import { join as join44, dirname as dirname18 } from "path";
151619
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17 } from "fs";
151620
+ function generatePhoneId() {
151621
+ return `ph_${generateId().slice(0, 12)}`;
151622
+ }
151623
+ function generateCallId() {
151624
+ return `call_${generateId().slice(0, 12)}`;
151625
+ }
151626
+ function generateSmsId() {
151627
+ return `sms_${generateId().slice(0, 12)}`;
151628
+ }
151629
+ function generateRuleId() {
151630
+ return `rule_${generateId().slice(0, 12)}`;
151631
+ }
151632
+
151633
+ class TelephonyStore {
151634
+ db;
151635
+ constructor(dbPath) {
151636
+ const baseDir = getConfigDir();
151637
+ const path2 = dbPath || join44(baseDir, "telephony.db");
151638
+ const dir = dirname18(path2);
151639
+ if (!existsSync27(dir)) {
151640
+ mkdirSync17(dir, { recursive: true });
151641
+ }
151642
+ const runtime = getRuntime();
151643
+ this.db = runtime.openDatabase(path2);
151644
+ this.initialize();
151645
+ }
151646
+ initialize() {
151647
+ this.db.exec(`
151648
+ CREATE TABLE IF NOT EXISTS phone_numbers (
151649
+ id TEXT PRIMARY KEY,
151650
+ number TEXT NOT NULL UNIQUE,
151651
+ friendly_name TEXT,
151652
+ twilio_sid TEXT,
151653
+ status TEXT NOT NULL DEFAULT 'active',
151654
+ voice_capable INTEGER NOT NULL DEFAULT 1,
151655
+ sms_capable INTEGER NOT NULL DEFAULT 1,
151656
+ whatsapp_capable INTEGER NOT NULL DEFAULT 0,
151657
+ created_at TEXT NOT NULL,
151658
+ updated_at TEXT NOT NULL
151659
+ );
151660
+
151661
+ CREATE TABLE IF NOT EXISTS call_logs (
151662
+ id TEXT PRIMARY KEY,
151663
+ call_sid TEXT,
151664
+ from_number TEXT NOT NULL,
151665
+ to_number TEXT NOT NULL,
151666
+ direction TEXT NOT NULL,
151667
+ status TEXT NOT NULL DEFAULT 'pending',
151668
+ assistant_id TEXT,
151669
+ duration INTEGER,
151670
+ recording_url TEXT,
151671
+ started_at TEXT,
151672
+ ended_at TEXT,
151673
+ created_at TEXT NOT NULL
151674
+ );
151675
+
151676
+ CREATE TABLE IF NOT EXISTS sms_logs (
151677
+ id TEXT PRIMARY KEY,
151678
+ message_sid TEXT,
151679
+ from_number TEXT NOT NULL,
151680
+ to_number TEXT NOT NULL,
151681
+ direction TEXT NOT NULL,
151682
+ message_type TEXT NOT NULL DEFAULT 'sms',
151683
+ body TEXT NOT NULL,
151684
+ status TEXT NOT NULL DEFAULT 'queued',
151685
+ assistant_id TEXT,
151686
+ created_at TEXT NOT NULL
151687
+ );
151688
+
151689
+ CREATE TABLE IF NOT EXISTS routing_rules (
151690
+ id TEXT PRIMARY KEY,
151691
+ name TEXT NOT NULL,
151692
+ priority INTEGER NOT NULL DEFAULT 100,
151693
+ from_pattern TEXT,
151694
+ to_pattern TEXT,
151695
+ message_type TEXT NOT NULL DEFAULT 'all',
151696
+ time_of_day TEXT,
151697
+ day_of_week TEXT,
151698
+ keyword TEXT,
151699
+ target_assistant_id TEXT NOT NULL,
151700
+ target_assistant_name TEXT NOT NULL,
151701
+ enabled INTEGER NOT NULL DEFAULT 1,
151702
+ created_at TEXT NOT NULL,
151703
+ updated_at TEXT NOT NULL
151704
+ );
151705
+
151706
+ CREATE INDEX IF NOT EXISTS idx_call_logs_assistant ON call_logs(assistant_id);
151707
+ CREATE INDEX IF NOT EXISTS idx_call_logs_status ON call_logs(status);
151708
+ CREATE INDEX IF NOT EXISTS idx_call_logs_created ON call_logs(created_at);
151709
+ CREATE INDEX IF NOT EXISTS idx_call_logs_sid ON call_logs(call_sid);
151710
+ CREATE INDEX IF NOT EXISTS idx_sms_logs_assistant ON sms_logs(assistant_id);
151711
+ CREATE INDEX IF NOT EXISTS idx_sms_logs_created ON sms_logs(created_at);
151712
+ CREATE INDEX IF NOT EXISTS idx_sms_logs_sid ON sms_logs(message_sid);
151713
+ CREATE INDEX IF NOT EXISTS idx_routing_rules_priority ON routing_rules(priority, enabled);
151714
+ `);
151715
+ }
151716
+ addPhoneNumber(number, friendlyName, twilioSid, capabilities) {
151717
+ const id = generatePhoneId();
151718
+ const now2 = new Date().toISOString();
151719
+ this.db.prepare(`INSERT INTO phone_numbers (id, number, friendly_name, twilio_sid, voice_capable, sms_capable, whatsapp_capable, created_at, updated_at)
151720
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, number, friendlyName, twilioSid, capabilities?.voice !== false ? 1 : 0, capabilities?.sms !== false ? 1 : 0, capabilities?.whatsapp ? 1 : 0, now2, now2);
151721
+ return this.getPhoneNumber(id);
151722
+ }
151723
+ getPhoneNumber(id) {
151724
+ const row = this.db.prepare("SELECT * FROM phone_numbers WHERE id = ?").get(id);
151725
+ return row ? this.rowToPhoneNumber(row) : null;
151726
+ }
151727
+ getPhoneNumberByNumber(number) {
151728
+ const row = this.db.prepare("SELECT * FROM phone_numbers WHERE number = ?").get(number);
151729
+ return row ? this.rowToPhoneNumber(row) : null;
151730
+ }
151731
+ listPhoneNumbers(status) {
151732
+ let query = "SELECT * FROM phone_numbers";
151733
+ const params = [];
151734
+ if (status) {
151735
+ query += " WHERE status = ?";
151736
+ params.push(status);
151737
+ }
151738
+ query += " ORDER BY created_at DESC";
151739
+ const rows = this.db.prepare(query).all(...params);
151740
+ return rows.map((r6) => this.rowToPhoneNumber(r6));
151741
+ }
151742
+ updatePhoneNumberStatus(id, status) {
151743
+ const now2 = new Date().toISOString();
151744
+ const result = this.db.prepare("UPDATE phone_numbers SET status = ?, updated_at = ? WHERE id = ?").run(status, now2, id);
151745
+ return result.changes > 0;
151746
+ }
151747
+ deletePhoneNumber(id) {
151748
+ const result = this.db.prepare("DELETE FROM phone_numbers WHERE id = ?").run(id);
151749
+ return result.changes > 0;
151750
+ }
151751
+ createCallLog(params) {
151752
+ const id = generateCallId();
151753
+ const now2 = new Date().toISOString();
151754
+ this.db.prepare(`INSERT INTO call_logs (id, call_sid, from_number, to_number, direction, status, assistant_id, created_at)
151755
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.callSid || null, params.fromNumber, params.toNumber, params.direction, params.status || "pending", params.assistantId || null, now2);
151756
+ return this.getCallLog(id);
151757
+ }
151758
+ getCallLog(id) {
151759
+ const row = this.db.prepare("SELECT * FROM call_logs WHERE id = ?").get(id);
151760
+ return row ? this.rowToCallLog(row) : null;
151761
+ }
151762
+ getCallLogBySid(callSid) {
151763
+ const row = this.db.prepare("SELECT * FROM call_logs WHERE call_sid = ?").get(callSid);
151764
+ return row ? this.rowToCallLog(row) : null;
151765
+ }
151766
+ updateCallLog(id, updates) {
151767
+ const sets = [];
151768
+ const params = [];
151769
+ if (updates.status !== undefined) {
151770
+ sets.push("status = ?");
151771
+ params.push(updates.status);
151772
+ }
151773
+ if (updates.callSid !== undefined) {
151774
+ sets.push("call_sid = ?");
151775
+ params.push(updates.callSid);
151776
+ }
151777
+ if (updates.duration !== undefined) {
151778
+ sets.push("duration = ?");
151779
+ params.push(updates.duration);
151780
+ }
151781
+ if (updates.recordingUrl !== undefined) {
151782
+ sets.push("recording_url = ?");
151783
+ params.push(updates.recordingUrl);
151784
+ }
151785
+ if (updates.startedAt !== undefined) {
151786
+ sets.push("started_at = ?");
151787
+ params.push(updates.startedAt);
151788
+ }
151789
+ if (updates.endedAt !== undefined) {
151790
+ sets.push("ended_at = ?");
151791
+ params.push(updates.endedAt);
151792
+ }
151793
+ if (sets.length === 0)
151794
+ return false;
151795
+ params.push(id);
151796
+ const result = this.db.prepare(`UPDATE call_logs SET ${sets.join(", ")} WHERE id = ?`).run(...params);
151797
+ return result.changes > 0;
151798
+ }
151799
+ listCallLogs(options) {
151800
+ const conditions = [];
151801
+ const params = [];
151802
+ const limit2 = options?.limit || 50;
151803
+ if (options?.assistantId) {
151804
+ conditions.push("assistant_id = ?");
151805
+ params.push(options.assistantId);
151806
+ }
151807
+ if (options?.direction) {
151808
+ conditions.push("direction = ?");
151809
+ params.push(options.direction);
151810
+ }
151811
+ if (options?.status) {
151812
+ conditions.push("status = ?");
151813
+ params.push(options.status);
151814
+ }
151815
+ let query = "SELECT * FROM call_logs";
151816
+ if (conditions.length > 0) {
151817
+ query += " WHERE " + conditions.join(" AND ");
151818
+ }
151819
+ query += " ORDER BY created_at DESC LIMIT ?";
151820
+ params.push(limit2);
151821
+ const rows = this.db.prepare(query).all(...params);
151822
+ return rows.map((row) => ({
151823
+ id: String(row.id),
151824
+ fromNumber: String(row.from_number),
151825
+ toNumber: String(row.to_number),
151826
+ direction: String(row.direction),
151827
+ status: String(row.status),
151828
+ duration: row.duration != null ? Number(row.duration) : null,
151829
+ startedAt: row.started_at ? String(row.started_at) : null,
151830
+ createdAt: String(row.created_at)
151831
+ }));
151832
+ }
151833
+ createSmsLog(params) {
151834
+ const id = generateSmsId();
151835
+ const now2 = new Date().toISOString();
151836
+ this.db.prepare(`INSERT INTO sms_logs (id, message_sid, from_number, to_number, direction, message_type, body, status, assistant_id, created_at)
151837
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.messageSid || null, params.fromNumber, params.toNumber, params.direction, params.messageType || "sms", params.body, params.status || "queued", params.assistantId || null, now2);
151838
+ return this.getSmsLog(id);
151839
+ }
151840
+ getSmsLog(id) {
151841
+ const row = this.db.prepare("SELECT * FROM sms_logs WHERE id = ?").get(id);
151842
+ return row ? this.rowToSmsLog(row) : null;
151843
+ }
151844
+ updateSmsStatus(id, status) {
151845
+ const result = this.db.prepare("UPDATE sms_logs SET status = ? WHERE id = ?").run(status, id);
151846
+ return result.changes > 0;
151847
+ }
151848
+ listSmsLogs(options) {
151849
+ const conditions = [];
151850
+ const params = [];
151851
+ const limit2 = options?.limit || 50;
151852
+ if (options?.assistantId) {
151853
+ conditions.push("assistant_id = ?");
151854
+ params.push(options.assistantId);
151855
+ }
151856
+ if (options?.direction) {
151857
+ conditions.push("direction = ?");
151858
+ params.push(options.direction);
151859
+ }
151860
+ if (options?.messageType) {
151861
+ conditions.push("message_type = ?");
151862
+ params.push(options.messageType);
151863
+ }
151864
+ let query = "SELECT * FROM sms_logs";
151865
+ if (conditions.length > 0) {
151866
+ query += " WHERE " + conditions.join(" AND ");
151867
+ }
151868
+ query += " ORDER BY created_at DESC LIMIT ?";
151869
+ params.push(limit2);
151870
+ const rows = this.db.prepare(query).all(...params);
151871
+ return rows.map((row) => ({
151872
+ id: String(row.id),
151873
+ fromNumber: String(row.from_number),
151874
+ toNumber: String(row.to_number),
151875
+ direction: String(row.direction),
151876
+ messageType: String(row.message_type),
151877
+ bodyPreview: String(row.body).slice(0, 100),
151878
+ status: String(row.status),
151879
+ createdAt: String(row.created_at)
151880
+ }));
151881
+ }
151882
+ getUnreadInboundSms(assistantId, limit2) {
151883
+ const maxLimit = limit2 || 50;
151884
+ const rows = this.db.prepare(`SELECT * FROM sms_logs
151885
+ WHERE direction = 'inbound' AND assistant_id = ? AND status = 'received'
151886
+ ORDER BY created_at ASC LIMIT ?`).all(assistantId, maxLimit);
151887
+ return rows.map((r6) => this.rowToSmsLog(r6));
151888
+ }
151889
+ createRoutingRule(params) {
151890
+ const id = generateRuleId();
151891
+ const now2 = new Date().toISOString();
151892
+ this.db.prepare(`INSERT INTO routing_rules (id, name, priority, from_pattern, to_pattern, message_type, time_of_day, day_of_week, keyword, target_assistant_id, target_assistant_name, created_at, updated_at)
151893
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.name, params.priority ?? 100, params.fromPattern || null, params.toPattern || null, params.messageType || "all", params.timeOfDay || null, params.dayOfWeek || null, params.keyword || null, params.targetAssistantId, params.targetAssistantName, now2, now2);
151894
+ return this.getRoutingRule(id);
151895
+ }
151896
+ getRoutingRule(id) {
151897
+ const row = this.db.prepare("SELECT * FROM routing_rules WHERE id = ?").get(id);
151898
+ return row ? this.rowToRoutingRule(row) : null;
151899
+ }
151900
+ listRoutingRules() {
151901
+ const rows = this.db.prepare("SELECT * FROM routing_rules ORDER BY priority ASC, created_at ASC").all();
151902
+ return rows.map((r6) => this.rowToRoutingRule(r6));
151903
+ }
151904
+ updateRoutingRule(id, updates) {
151905
+ const sets = [];
151906
+ const params = [];
151907
+ const now2 = new Date().toISOString();
151908
+ if (updates.name !== undefined) {
151909
+ sets.push("name = ?");
151910
+ params.push(updates.name);
151911
+ }
151912
+ if (updates.priority !== undefined) {
151913
+ sets.push("priority = ?");
151914
+ params.push(updates.priority);
151915
+ }
151916
+ if (updates.enabled !== undefined) {
151917
+ sets.push("enabled = ?");
151918
+ params.push(updates.enabled ? 1 : 0);
151919
+ }
151920
+ sets.push("updated_at = ?");
151921
+ params.push(now2);
151922
+ if (sets.length === 1)
151923
+ return false;
151924
+ params.push(id);
151925
+ const result = this.db.prepare(`UPDATE routing_rules SET ${sets.join(", ")} WHERE id = ?`).run(...params);
151926
+ return result.changes > 0;
151927
+ }
151928
+ deleteRoutingRule(id) {
151929
+ const result = this.db.prepare("DELETE FROM routing_rules WHERE id = ?").run(id);
151930
+ return result.changes > 0;
151931
+ }
151932
+ resolveRouting(params) {
151933
+ const rules = this.db.prepare(`SELECT * FROM routing_rules WHERE enabled = 1 ORDER BY priority ASC`).all();
151934
+ for (const row of rules) {
151935
+ const rule = this.rowToRoutingRule(row);
151936
+ if (rule.messageType !== "all" && rule.messageType !== params.messageType)
151937
+ continue;
151938
+ if (rule.fromPattern && !matchPattern2(rule.fromPattern, params.fromNumber))
151939
+ continue;
151940
+ if (rule.toPattern && !matchPattern2(rule.toPattern, params.toNumber))
151941
+ continue;
151942
+ if (rule.keyword && params.body) {
151943
+ if (!params.body.toLowerCase().includes(rule.keyword.toLowerCase()))
151944
+ continue;
151945
+ } else if (rule.keyword && !params.body) {
151946
+ continue;
151947
+ }
151948
+ if (rule.timeOfDay && !matchTimeOfDay(rule.timeOfDay))
151949
+ continue;
151950
+ if (rule.dayOfWeek && !matchDayOfWeek(rule.dayOfWeek))
151951
+ continue;
151952
+ return {
151953
+ assistantId: rule.targetAssistantId,
151954
+ assistantName: rule.targetAssistantName,
151955
+ ruleId: rule.id
151956
+ };
151957
+ }
151958
+ return null;
151959
+ }
151960
+ cleanup(maxAgeDays, maxCallLogs, maxSmsLogs) {
151961
+ let deleted = 0;
151962
+ const cutoff = new Date;
151963
+ cutoff.setDate(cutoff.getDate() - maxAgeDays);
151964
+ const cutoffStr = cutoff.toISOString();
151965
+ const callResult = this.db.prepare("DELETE FROM call_logs WHERE created_at < ?").run(cutoffStr);
151966
+ deleted += callResult.changes;
151967
+ const smsResult = this.db.prepare("DELETE FROM sms_logs WHERE created_at < ?").run(cutoffStr);
151968
+ deleted += smsResult.changes;
151969
+ const callCount = this.db.prepare("SELECT COUNT(*) as cnt FROM call_logs").get();
151970
+ if (Number(callCount.cnt) > maxCallLogs) {
151971
+ const excess = Number(callCount.cnt) - maxCallLogs;
151972
+ const trimResult = this.db.prepare(`DELETE FROM call_logs WHERE id IN (
151973
+ SELECT id FROM call_logs ORDER BY created_at ASC LIMIT ?
151974
+ )`).run(excess);
151975
+ deleted += trimResult.changes;
151976
+ }
151977
+ const smsCount = this.db.prepare("SELECT COUNT(*) as cnt FROM sms_logs").get();
151978
+ if (Number(smsCount.cnt) > maxSmsLogs) {
151979
+ const excess = Number(smsCount.cnt) - maxSmsLogs;
151980
+ const trimResult = this.db.prepare(`DELETE FROM sms_logs WHERE id IN (
151981
+ SELECT id FROM sms_logs ORDER BY created_at ASC LIMIT ?
151982
+ )`).run(excess);
151983
+ deleted += trimResult.changes;
151984
+ }
151985
+ return deleted;
151986
+ }
151987
+ close() {
151988
+ try {
151989
+ this.db.close();
151990
+ } catch {}
151991
+ }
151992
+ rowToPhoneNumber(row) {
151993
+ return {
151994
+ id: String(row.id),
151995
+ number: String(row.number),
151996
+ friendlyName: row.friendly_name ? String(row.friendly_name) : null,
151997
+ twilioSid: row.twilio_sid ? String(row.twilio_sid) : null,
151998
+ status: String(row.status),
151999
+ capabilities: {
152000
+ voice: Boolean(row.voice_capable),
152001
+ sms: Boolean(row.sms_capable),
152002
+ whatsapp: Boolean(row.whatsapp_capable)
152003
+ },
152004
+ createdAt: String(row.created_at),
152005
+ updatedAt: String(row.updated_at)
152006
+ };
152007
+ }
152008
+ rowToCallLog(row) {
152009
+ return {
152010
+ id: String(row.id),
152011
+ callSid: row.call_sid ? String(row.call_sid) : null,
152012
+ fromNumber: String(row.from_number),
152013
+ toNumber: String(row.to_number),
152014
+ direction: String(row.direction),
152015
+ status: String(row.status),
152016
+ assistantId: row.assistant_id ? String(row.assistant_id) : null,
152017
+ duration: row.duration != null ? Number(row.duration) : null,
152018
+ recordingUrl: row.recording_url ? String(row.recording_url) : null,
152019
+ startedAt: row.started_at ? String(row.started_at) : null,
152020
+ endedAt: row.ended_at ? String(row.ended_at) : null,
152021
+ createdAt: String(row.created_at)
152022
+ };
152023
+ }
152024
+ rowToSmsLog(row) {
152025
+ return {
152026
+ id: String(row.id),
152027
+ messageSid: row.message_sid ? String(row.message_sid) : null,
152028
+ fromNumber: String(row.from_number),
152029
+ toNumber: String(row.to_number),
152030
+ direction: String(row.direction),
152031
+ messageType: String(row.message_type),
152032
+ body: String(row.body),
152033
+ status: String(row.status),
152034
+ assistantId: row.assistant_id ? String(row.assistant_id) : null,
152035
+ createdAt: String(row.created_at)
152036
+ };
152037
+ }
152038
+ rowToRoutingRule(row) {
152039
+ return {
152040
+ id: String(row.id),
152041
+ name: String(row.name),
152042
+ priority: Number(row.priority),
152043
+ fromPattern: row.from_pattern ? String(row.from_pattern) : null,
152044
+ toPattern: row.to_pattern ? String(row.to_pattern) : null,
152045
+ messageType: String(row.message_type),
152046
+ timeOfDay: row.time_of_day ? String(row.time_of_day) : null,
152047
+ dayOfWeek: row.day_of_week ? String(row.day_of_week) : null,
152048
+ keyword: row.keyword ? String(row.keyword) : null,
152049
+ targetAssistantId: String(row.target_assistant_id),
152050
+ targetAssistantName: String(row.target_assistant_name),
152051
+ enabled: Boolean(row.enabled),
152052
+ createdAt: String(row.created_at),
152053
+ updatedAt: String(row.updated_at)
152054
+ };
152055
+ }
152056
+ }
152057
+ function matchPattern2(pattern, value) {
152058
+ if (pattern === "*")
152059
+ return true;
152060
+ if (pattern.endsWith("*")) {
152061
+ return value.startsWith(pattern.slice(0, -1));
152062
+ }
152063
+ if (pattern.startsWith("*")) {
152064
+ return value.endsWith(pattern.slice(1));
152065
+ }
152066
+ return pattern === value;
152067
+ }
152068
+ function matchTimeOfDay(timeRange) {
152069
+ const [start, end] = timeRange.split("-");
152070
+ if (!start || !end)
152071
+ return true;
152072
+ const now2 = new Date;
152073
+ const currentMinutes = now2.getHours() * 60 + now2.getMinutes();
152074
+ const [startH, startM] = start.split(":").map(Number);
152075
+ const [endH, endM] = end.split(":").map(Number);
152076
+ const startMinutes = startH * 60 + startM;
152077
+ const endMinutes = endH * 60 + endM;
152078
+ if (startMinutes <= endMinutes) {
152079
+ return currentMinutes >= startMinutes && currentMinutes <= endMinutes;
152080
+ }
152081
+ return currentMinutes >= startMinutes || currentMinutes <= endMinutes;
152082
+ }
152083
+ function matchDayOfWeek(dayList) {
152084
+ const days = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
152085
+ const today = days[new Date().getDay()];
152086
+ const allowedDays = dayList.toLowerCase().split(",").map((d5) => d5.trim());
152087
+ return allowedDays.includes(today);
152088
+ }
152089
+
152090
+ // ../core/src/telephony/twilio-client.ts
152091
+ var TWILIO_API_BASE = "https://api.twilio.com/2010-04-01";
152092
+
152093
+ class TwilioClient {
152094
+ accountSid;
152095
+ authToken;
152096
+ authHeader;
152097
+ constructor(config) {
152098
+ this.accountSid = config.accountSid;
152099
+ this.authToken = config.authToken;
152100
+ this.authHeader = "Basic " + Buffer.from(`${this.accountSid}:${this.authToken}`).toString("base64");
152101
+ }
152102
+ isConfigured() {
152103
+ return Boolean(this.accountSid && this.authToken);
152104
+ }
152105
+ async makeCall(params) {
152106
+ const body = new URLSearchParams;
152107
+ body.append("To", params.to);
152108
+ body.append("From", params.from);
152109
+ if (params.url) {
152110
+ body.append("Url", params.url);
152111
+ } else if (params.twiml) {
152112
+ body.append("Twiml", params.twiml);
152113
+ }
152114
+ if (params.statusCallback) {
152115
+ body.append("StatusCallback", params.statusCallback);
152116
+ body.append("StatusCallbackEvent", "initiated ringing answered completed");
152117
+ body.append("StatusCallbackMethod", "POST");
152118
+ }
152119
+ if (params.record) {
152120
+ body.append("Record", "true");
152121
+ }
152122
+ return this.post(`/Accounts/${this.accountSid}/Calls.json`, body);
152123
+ }
152124
+ async updateCall(callSid, updates) {
152125
+ const body = new URLSearchParams;
152126
+ if (updates.status) {
152127
+ body.append("Status", updates.status);
152128
+ }
152129
+ if (updates.url) {
152130
+ body.append("Url", updates.url);
152131
+ }
152132
+ if (updates.twiml) {
152133
+ body.append("Twiml", updates.twiml);
152134
+ }
152135
+ return this.post(`/Accounts/${this.accountSid}/Calls/${callSid}.json`, body);
152136
+ }
152137
+ async getCall(callSid) {
152138
+ return this.get(`/Accounts/${this.accountSid}/Calls/${callSid}.json`);
152139
+ }
152140
+ async sendSms(params) {
152141
+ const body = new URLSearchParams;
152142
+ body.append("To", params.to);
152143
+ body.append("From", params.from);
152144
+ body.append("Body", params.body);
152145
+ if (params.statusCallback) {
152146
+ body.append("StatusCallback", params.statusCallback);
152147
+ }
152148
+ return this.post(`/Accounts/${this.accountSid}/Messages.json`, body);
152149
+ }
152150
+ async sendWhatsApp(params) {
152151
+ const to3 = params.to.startsWith("whatsapp:") ? params.to : `whatsapp:${params.to}`;
152152
+ const from = params.from.startsWith("whatsapp:") ? params.from : `whatsapp:${params.from}`;
152153
+ return this.sendSms({
152154
+ ...params,
152155
+ to: to3,
152156
+ from
152157
+ });
152158
+ }
152159
+ async listPhoneNumbers() {
152160
+ return this.get(`/Accounts/${this.accountSid}/IncomingPhoneNumbers.json`);
152161
+ }
152162
+ async getPhoneNumber(phoneSid) {
152163
+ return this.get(`/Accounts/${this.accountSid}/IncomingPhoneNumbers/${phoneSid}.json`);
152164
+ }
152165
+ async updatePhoneNumber(phoneSid, updates) {
152166
+ const body = new URLSearchParams;
152167
+ if (updates.voiceUrl) {
152168
+ body.append("VoiceUrl", updates.voiceUrl);
152169
+ body.append("VoiceMethod", updates.voiceMethod || "POST");
152170
+ }
152171
+ if (updates.smsUrl) {
152172
+ body.append("SmsUrl", updates.smsUrl);
152173
+ body.append("SmsMethod", updates.smsMethod || "POST");
152174
+ }
152175
+ if (updates.statusCallback) {
152176
+ body.append("StatusCallback", updates.statusCallback);
152177
+ }
152178
+ return this.post(`/Accounts/${this.accountSid}/IncomingPhoneNumbers/${phoneSid}.json`, body);
152179
+ }
152180
+ async verifyCredentials() {
152181
+ return this.get(`/Accounts/${this.accountSid}.json`);
152182
+ }
152183
+ async get(path2) {
152184
+ try {
152185
+ const response = await fetch(`${TWILIO_API_BASE}${path2}`, {
152186
+ method: "GET",
152187
+ headers: {
152188
+ Authorization: this.authHeader,
152189
+ Accept: "application/json"
152190
+ }
152191
+ });
152192
+ const data = await response.json();
152193
+ if (!response.ok) {
152194
+ return {
152195
+ success: false,
152196
+ error: data.message || `HTTP ${response.status}`,
152197
+ statusCode: response.status
152198
+ };
152199
+ }
152200
+ return { success: true, data, statusCode: response.status };
152201
+ } catch (error2) {
152202
+ return {
152203
+ success: false,
152204
+ error: error2 instanceof Error ? error2.message : String(error2),
152205
+ statusCode: 0
152206
+ };
152207
+ }
152208
+ }
152209
+ async post(path2, body) {
152210
+ try {
152211
+ const response = await fetch(`${TWILIO_API_BASE}${path2}`, {
152212
+ method: "POST",
152213
+ headers: {
152214
+ Authorization: this.authHeader,
152215
+ "Content-Type": "application/x-www-form-urlencoded",
152216
+ Accept: "application/json"
152217
+ },
152218
+ body: body.toString()
152219
+ });
152220
+ const data = await response.json();
152221
+ if (!response.ok) {
152222
+ return {
152223
+ success: false,
152224
+ error: data.message || `HTTP ${response.status}`,
152225
+ statusCode: response.status
152226
+ };
152227
+ }
152228
+ return { success: true, data, statusCode: response.status };
152229
+ } catch (error2) {
152230
+ return {
152231
+ success: false,
152232
+ error: error2 instanceof Error ? error2.message : String(error2),
152233
+ statusCode: 0
152234
+ };
152235
+ }
152236
+ }
152237
+ }
152238
+
152239
+ // ../core/src/telephony/call-manager.ts
152240
+ class CallManager {
152241
+ activeCalls = new Map;
152242
+ maxCallDurationMs;
152243
+ staleTimeoutMs;
152244
+ constructor(config) {
152245
+ this.maxCallDurationMs = (config?.maxCallDurationSeconds || 3600) * 1000;
152246
+ this.staleTimeoutMs = (config?.staleTimeoutSeconds || 300) * 1000;
152247
+ }
152248
+ addCall(params) {
152249
+ const call = {
152250
+ callSid: params.callSid,
152251
+ streamSid: null,
152252
+ fromNumber: params.fromNumber,
152253
+ toNumber: params.toNumber,
152254
+ direction: params.direction,
152255
+ state: "connecting",
152256
+ assistantId: params.assistantId || null,
152257
+ bridgeId: null,
152258
+ startedAt: Date.now(),
152259
+ lastActivityAt: Date.now()
152260
+ };
152261
+ this.activeCalls.set(params.callSid, call);
152262
+ return call;
152263
+ }
152264
+ getCall(callSid) {
152265
+ return this.activeCalls.get(callSid) || null;
152266
+ }
152267
+ updateState(callSid, state) {
152268
+ const call = this.activeCalls.get(callSid);
152269
+ if (!call)
152270
+ return false;
152271
+ const validTransitions = {
152272
+ connecting: ["ringing", "active", "ending"],
152273
+ ringing: ["bridging", "active", "ending"],
152274
+ bridging: ["active", "ending"],
152275
+ active: ["ending"],
152276
+ ending: []
152277
+ };
152278
+ if (!validTransitions[call.state].includes(state)) {
152279
+ return false;
152280
+ }
152281
+ call.state = state;
152282
+ call.lastActivityAt = Date.now();
152283
+ return true;
152284
+ }
152285
+ setStreamSid(callSid, streamSid) {
152286
+ const call = this.activeCalls.get(callSid);
152287
+ if (!call)
152288
+ return false;
152289
+ call.streamSid = streamSid;
152290
+ call.lastActivityAt = Date.now();
152291
+ return true;
152292
+ }
152293
+ setBridgeId(callSid, bridgeId) {
152294
+ const call = this.activeCalls.get(callSid);
152295
+ if (!call)
152296
+ return false;
152297
+ call.bridgeId = bridgeId;
152298
+ call.lastActivityAt = Date.now();
152299
+ return true;
152300
+ }
152301
+ touchCall(callSid) {
152302
+ const call = this.activeCalls.get(callSid);
152303
+ if (call) {
152304
+ call.lastActivityAt = Date.now();
152305
+ }
152306
+ }
152307
+ endCall(callSid) {
152308
+ const call = this.activeCalls.get(callSid);
152309
+ if (!call)
152310
+ return null;
152311
+ call.state = "ending";
152312
+ this.activeCalls.delete(callSid);
152313
+ return call;
152314
+ }
152315
+ getActiveCalls() {
152316
+ return Array.from(this.activeCalls.values());
152317
+ }
152318
+ getActiveCallCount() {
152319
+ return this.activeCalls.size;
152320
+ }
152321
+ getCallByStreamSid(streamSid) {
152322
+ for (const call of this.activeCalls.values()) {
152323
+ if (call.streamSid === streamSid)
152324
+ return call;
152325
+ }
152326
+ return null;
152327
+ }
152328
+ cleanupStaleCalls() {
152329
+ const now2 = Date.now();
152330
+ const removed = [];
152331
+ for (const [callSid, call] of this.activeCalls) {
152332
+ if (now2 - call.startedAt > this.maxCallDurationMs) {
152333
+ this.activeCalls.delete(callSid);
152334
+ removed.push(callSid);
152335
+ continue;
152336
+ }
152337
+ if (now2 - call.lastActivityAt > this.staleTimeoutMs) {
152338
+ this.activeCalls.delete(callSid);
152339
+ removed.push(callSid);
152340
+ }
152341
+ }
152342
+ return removed;
152343
+ }
152344
+ getCallDuration(callSid) {
152345
+ const call = this.activeCalls.get(callSid);
152346
+ if (!call)
152347
+ return null;
152348
+ return Math.floor((Date.now() - call.startedAt) / 1000);
152349
+ }
152350
+ endAllCalls() {
152351
+ const calls = Array.from(this.activeCalls.values());
152352
+ this.activeCalls.clear();
152353
+ return calls;
152354
+ }
152355
+ }
152356
+
152357
+ // ../core/src/telephony/voice-bridge.ts
152358
+ init_src2();
152359
+
152360
+ // ../core/src/telephony/audio-codec.ts
152361
+ var MULAW_MAX = 8191;
152362
+ var MULAW_BIAS = 33;
152363
+ function pcmSampleToMulaw(sample) {
152364
+ const sign2 = sample >> 8 & 128;
152365
+ if (sign2 !== 0)
152366
+ sample = -sample;
152367
+ if (sample > MULAW_MAX)
152368
+ sample = MULAW_MAX;
152369
+ sample += MULAW_BIAS;
152370
+ let exponent = 7;
152371
+ let mask = 16384;
152372
+ while (exponent > 0 && (sample & mask) === 0) {
152373
+ exponent--;
152374
+ mask >>= 1;
152375
+ }
152376
+ const mantissa = sample >> exponent + 3 & 15;
152377
+ const mulawByte = ~(sign2 | exponent << 4 | mantissa) & 255;
152378
+ return mulawByte;
152379
+ }
152380
+ function mulawSampleToPcm(mulawByte) {
152381
+ mulawByte = ~mulawByte & 255;
152382
+ const sign2 = mulawByte & 128;
152383
+ const exponent = mulawByte >> 4 & 7;
152384
+ const mantissa = mulawByte & 15;
152385
+ let sample = (mantissa << 3) + MULAW_BIAS << exponent;
152386
+ sample -= MULAW_BIAS;
152387
+ return sign2 !== 0 ? -sample : sample;
152388
+ }
152389
+ function pcmToMulaw(pcmBuffer) {
152390
+ const numSamples = Math.floor(pcmBuffer.length / 2);
152391
+ const mulawBuffer = Buffer.alloc(numSamples);
152392
+ for (let i5 = 0;i5 < numSamples; i5++) {
152393
+ const sample = pcmBuffer.readInt16LE(i5 * 2);
152394
+ mulawBuffer[i5] = pcmSampleToMulaw(sample);
152395
+ }
152396
+ return mulawBuffer;
152397
+ }
152398
+ function mulawToPcm(mulawBuffer) {
152399
+ const pcmBuffer = Buffer.alloc(mulawBuffer.length * 2);
152400
+ for (let i5 = 0;i5 < mulawBuffer.length; i5++) {
152401
+ const sample = mulawSampleToPcm(mulawBuffer[i5]);
152402
+ pcmBuffer.writeInt16LE(sample, i5 * 2);
152403
+ }
152404
+ return pcmBuffer;
152405
+ }
152406
+ function downsample16kTo8k(pcm16k) {
152407
+ const numSamples = Math.floor(pcm16k.length / 2);
152408
+ const outputSamples = Math.floor(numSamples / 2);
152409
+ const pcm8k = Buffer.alloc(outputSamples * 2);
152410
+ for (let i5 = 0;i5 < outputSamples; i5++) {
152411
+ const sample = pcm16k.readInt16LE(i5 * 2 * 2);
152412
+ pcm8k.writeInt16LE(sample, i5 * 2);
152413
+ }
152414
+ return pcm8k;
152415
+ }
152416
+ function upsample8kTo16k(pcm8k) {
152417
+ const numSamples = Math.floor(pcm8k.length / 2);
152418
+ const pcm16k = Buffer.alloc(numSamples * 2 * 2);
152419
+ for (let i5 = 0;i5 < numSamples; i5++) {
152420
+ const current = pcm8k.readInt16LE(i5 * 2);
152421
+ const next = i5 + 1 < numSamples ? pcm8k.readInt16LE((i5 + 1) * 2) : current;
152422
+ const interpolated = Math.round((current + next) / 2);
152423
+ pcm16k.writeInt16LE(current, i5 * 4);
152424
+ pcm16k.writeInt16LE(interpolated, i5 * 4 + 2);
152425
+ }
152426
+ return pcm16k;
152427
+ }
152428
+ function twilioToElevenLabs(mulawBuffer) {
152429
+ const pcm8k = mulawToPcm(mulawBuffer);
152430
+ return upsample8kTo16k(pcm8k);
152431
+ }
152432
+ function elevenLabsToTwilio(pcm16k) {
152433
+ const pcm8k = downsample16kTo8k(pcm16k);
152434
+ return pcmToMulaw(pcm8k);
152435
+ }
152436
+ function decodeTwilioPayload(base64Payload) {
152437
+ const mulawBuffer = Buffer.from(base64Payload, "base64");
152438
+ return twilioToElevenLabs(mulawBuffer);
152439
+ }
152440
+ function encodeTwilioPayload(pcm16k) {
152441
+ const mulawBuffer = elevenLabsToTwilio(pcm16k);
152442
+ return mulawBuffer.toString("base64");
152443
+ }
152444
+
152445
+ // ../core/src/telephony/voice-bridge.ts
152446
+ var ELEVENLABS_WS_BASE = "wss://api.elevenlabs.io/v1/convai/conversation";
152447
+
152448
+ class VoiceBridge {
152449
+ config;
152450
+ connections = new Map;
152451
+ constructor(config) {
152452
+ this.config = config;
152453
+ }
152454
+ isConfigured() {
152455
+ return Boolean(this.config.elevenLabsApiKey && this.config.elevenLabsAgentId);
152456
+ }
152457
+ async createBridge(callSid, streamSid, sendToTwilio) {
152458
+ const id = `bridge_${generateId().slice(0, 12)}`;
152459
+ const connection = new BridgeConnection({
152460
+ id,
152461
+ callSid,
152462
+ streamSid,
152463
+ config: this.config,
152464
+ sendToTwilio
152465
+ });
152466
+ this.connections.set(id, connection);
152467
+ try {
152468
+ await connection.connect();
152469
+ } catch (error2) {
152470
+ this.connections.delete(id);
152471
+ throw error2;
152472
+ }
152473
+ return id;
152474
+ }
152475
+ handleTwilioMedia(bridgeId, message) {
152476
+ const connection = this.connections.get(bridgeId);
152477
+ if (!connection)
152478
+ return;
152479
+ if (message.event === "media" && message.media?.payload) {
152480
+ connection.forwardToElevenLabs(message.media.payload);
152481
+ } else if (message.event === "stop") {
152482
+ connection.close();
152483
+ this.connections.delete(bridgeId);
152484
+ }
152485
+ }
152486
+ closeBridge(bridgeId) {
152487
+ const connection = this.connections.get(bridgeId);
152488
+ if (connection) {
152489
+ connection.close();
152490
+ this.connections.delete(bridgeId);
152491
+ }
152492
+ }
152493
+ getConnection(bridgeId) {
152494
+ const conn = this.connections.get(bridgeId);
152495
+ if (!conn)
152496
+ return null;
152497
+ return conn.getInfo();
152498
+ }
152499
+ getActiveConnections() {
152500
+ return Array.from(this.connections.values()).map((c6) => c6.getInfo());
152501
+ }
152502
+ closeAll() {
152503
+ for (const connection of this.connections.values()) {
152504
+ connection.close();
152505
+ }
152506
+ this.connections.clear();
152507
+ }
152508
+ }
152509
+
152510
+ class BridgeConnection {
152511
+ id;
152512
+ callSid;
152513
+ streamSid;
152514
+ config;
152515
+ sendToTwilio;
152516
+ elevenLabsWs = null;
152517
+ state = "connecting";
152518
+ startedAt;
152519
+ constructor(options) {
152520
+ this.id = options.id;
152521
+ this.callSid = options.callSid;
152522
+ this.streamSid = options.streamSid;
152523
+ this.config = options.config;
152524
+ this.sendToTwilio = options.sendToTwilio;
152525
+ this.startedAt = Date.now();
152526
+ }
152527
+ async connect() {
152528
+ const url = `${ELEVENLABS_WS_BASE}?agent_id=${this.config.elevenLabsAgentId}`;
152529
+ return new Promise((resolve5, reject) => {
152530
+ try {
152531
+ this.elevenLabsWs = new WebSocket(url, {
152532
+ headers: {
152533
+ "xi-api-key": this.config.elevenLabsApiKey
152534
+ }
152535
+ });
152536
+ this.elevenLabsWs.onopen = () => {
152537
+ this.state = "active";
152538
+ resolve5();
152539
+ };
152540
+ this.elevenLabsWs.onmessage = (event) => {
152541
+ this.handleElevenLabsMessage(event.data);
152542
+ };
152543
+ this.elevenLabsWs.onerror = (event) => {
152544
+ console.error(`[VoiceBridge ${this.id}] ElevenLabs WS error:`, event);
152545
+ if (this.state === "connecting") {
152546
+ reject(new Error("Failed to connect to ElevenLabs"));
152547
+ }
152548
+ };
152549
+ this.elevenLabsWs.onclose = () => {
152550
+ this.state = "closed";
152551
+ };
152552
+ setTimeout(() => {
152553
+ if (this.state === "connecting") {
152554
+ this.close();
152555
+ reject(new Error("ElevenLabs connection timeout"));
152556
+ }
152557
+ }, 1e4);
152558
+ } catch (error2) {
152559
+ reject(error2);
152560
+ }
152561
+ });
152562
+ }
152563
+ forwardToElevenLabs(base64Payload) {
152564
+ if (this.state !== "active" || !this.elevenLabsWs)
152565
+ return;
152566
+ try {
152567
+ const pcm16k = decodeTwilioPayload(base64Payload);
152568
+ const message = JSON.stringify({
152569
+ user_audio_chunk: pcm16k.toString("base64")
152570
+ });
152571
+ if (this.elevenLabsWs.readyState === WebSocket.OPEN) {
152572
+ this.elevenLabsWs.send(message);
152573
+ }
152574
+ } catch (error2) {
152575
+ console.error(`[VoiceBridge ${this.id}] Error forwarding to ElevenLabs:`, error2);
152576
+ }
152577
+ }
152578
+ handleElevenLabsMessage(data) {
152579
+ try {
152580
+ const message = JSON.parse(data);
152581
+ if (message.audio?.chunk) {
152582
+ const pcm16k = Buffer.from(message.audio.chunk, "base64");
152583
+ const twilioPayload = encodeTwilioPayload(pcm16k);
152584
+ const twilioMessage = JSON.stringify({
152585
+ event: "media",
152586
+ streamSid: this.streamSid,
152587
+ media: {
152588
+ payload: twilioPayload
152589
+ }
152590
+ });
152591
+ this.sendToTwilio(twilioMessage);
152592
+ }
152593
+ if (message.type === "conversation_initiation_metadata") {} else if (message.type === "agent_response") {} else if (message.type === "user_transcript") {}
152594
+ } catch (error2) {
152595
+ console.error(`[VoiceBridge ${this.id}] Error handling ElevenLabs message:`, error2);
152596
+ }
152597
+ }
152598
+ close() {
152599
+ if (this.state === "closed" || this.state === "closing")
152600
+ return;
152601
+ this.state = "closing";
152602
+ if (this.elevenLabsWs) {
152603
+ try {
152604
+ this.elevenLabsWs.close();
152605
+ } catch {}
152606
+ this.elevenLabsWs = null;
152607
+ }
152608
+ this.state = "closed";
152609
+ }
152610
+ getInfo() {
152611
+ return {
152612
+ id: this.id,
152613
+ callSid: this.callSid,
152614
+ streamSid: this.streamSid,
152615
+ state: this.state,
152616
+ startedAt: this.startedAt
152617
+ };
152618
+ }
152619
+ }
152620
+
152621
+ // ../core/src/telephony/manager.ts
152622
+ class TelephonyManager {
152623
+ assistantId;
152624
+ assistantName;
152625
+ config;
152626
+ store;
152627
+ twilioClient = null;
152628
+ callManager;
152629
+ voiceBridge = null;
152630
+ constructor(options) {
152631
+ this.assistantId = options.assistantId;
152632
+ this.assistantName = options.assistantName;
152633
+ this.config = options.config;
152634
+ this.store = new TelephonyStore;
152635
+ this.callManager = new CallManager({
152636
+ maxCallDurationSeconds: options.config.voice?.maxCallDurationSeconds
152637
+ });
152638
+ const accountSid = process.env.TWILIO_ACCOUNT_SID;
152639
+ const authToken = process.env.TWILIO_AUTH_TOKEN;
152640
+ if (accountSid && authToken) {
152641
+ this.twilioClient = new TwilioClient({ accountSid, authToken });
152642
+ }
152643
+ const elevenLabsApiKey = process.env.ELEVENLABS_API_KEY;
152644
+ const elevenLabsAgentId = this.config.elevenLabsAgentId || process.env.ELEVENLABS_AGENT_ID;
152645
+ if (elevenLabsApiKey && elevenLabsAgentId) {
152646
+ this.voiceBridge = new VoiceBridge({
152647
+ elevenLabsApiKey,
152648
+ elevenLabsAgentId
152649
+ });
152650
+ }
152651
+ }
152652
+ async sendSms(to3, body, from) {
152653
+ if (!this.twilioClient) {
152654
+ return {
152655
+ success: false,
152656
+ message: "Twilio is not configured. Set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN."
152657
+ };
152658
+ }
152659
+ const fromNumber = from || this.config.defaultPhoneNumber || process.env.TWILIO_PHONE_NUMBER;
152660
+ if (!fromNumber) {
152661
+ return {
152662
+ success: false,
152663
+ message: "No phone number configured. Set telephony.defaultPhoneNumber or TWILIO_PHONE_NUMBER."
152664
+ };
152665
+ }
152666
+ const webhookUrl = this.config.webhookUrl || process.env.TELEPHONY_WEBHOOK_URL;
152667
+ const statusCallback = webhookUrl ? `${webhookUrl}/api/v1/telephony/webhooks/sms-status` : undefined;
152668
+ const result = await this.twilioClient.sendSms({
152669
+ to: to3,
152670
+ from: fromNumber,
152671
+ body,
152672
+ statusCallback
152673
+ });
152674
+ if (!result.success) {
152675
+ return { success: false, message: `Failed to send SMS: ${result.error}` };
152676
+ }
152677
+ const log2 = this.store.createSmsLog({
152678
+ messageSid: result.data?.sid,
152679
+ fromNumber,
152680
+ toNumber: to3,
152681
+ direction: "outbound",
152682
+ messageType: "sms",
152683
+ body,
152684
+ status: "queued",
152685
+ assistantId: this.assistantId
152686
+ });
152687
+ return {
152688
+ success: true,
152689
+ message: `SMS sent to ${to3}.`,
152690
+ messageSid: result.data?.sid,
152691
+ id: log2.id
152692
+ };
152693
+ }
152694
+ async sendWhatsApp(to3, body, from) {
152695
+ if (!this.twilioClient) {
152696
+ return {
152697
+ success: false,
152698
+ message: "Twilio is not configured. Set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN."
152699
+ };
152700
+ }
152701
+ const fromNumber = from || this.config.defaultPhoneNumber || process.env.TWILIO_PHONE_NUMBER;
152702
+ if (!fromNumber) {
152703
+ return {
152704
+ success: false,
152705
+ message: "No phone number configured."
152706
+ };
152707
+ }
152708
+ const webhookUrl = this.config.webhookUrl || process.env.TELEPHONY_WEBHOOK_URL;
152709
+ const statusCallback = webhookUrl ? `${webhookUrl}/api/v1/telephony/webhooks/sms-status` : undefined;
152710
+ const result = await this.twilioClient.sendWhatsApp({
152711
+ to: to3,
152712
+ from: fromNumber,
152713
+ body,
152714
+ statusCallback
152715
+ });
152716
+ if (!result.success) {
152717
+ return { success: false, message: `Failed to send WhatsApp: ${result.error}` };
152718
+ }
152719
+ const log2 = this.store.createSmsLog({
152720
+ messageSid: result.data?.sid,
152721
+ fromNumber: `whatsapp:${fromNumber}`,
152722
+ toNumber: `whatsapp:${to3}`,
152723
+ direction: "outbound",
152724
+ messageType: "whatsapp",
152725
+ body,
152726
+ status: "queued",
152727
+ assistantId: this.assistantId
152728
+ });
152729
+ return {
152730
+ success: true,
152731
+ message: `WhatsApp message sent to ${to3}.`,
152732
+ messageSid: result.data?.sid,
152733
+ id: log2.id
152734
+ };
152735
+ }
152736
+ async makeCall(to3, from) {
152737
+ if (!this.twilioClient) {
152738
+ return {
152739
+ success: false,
152740
+ message: "Twilio is not configured. Set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN."
152741
+ };
152742
+ }
152743
+ const fromNumber = from || this.config.defaultPhoneNumber || process.env.TWILIO_PHONE_NUMBER;
152744
+ if (!fromNumber) {
152745
+ return {
152746
+ success: false,
152747
+ message: "No phone number configured."
152748
+ };
152749
+ }
152750
+ const webhookUrl = this.config.webhookUrl || process.env.TELEPHONY_WEBHOOK_URL;
152751
+ if (!webhookUrl) {
152752
+ return {
152753
+ success: false,
152754
+ message: "No webhook URL configured. Set telephony.webhookUrl or TELEPHONY_WEBHOOK_URL."
152755
+ };
152756
+ }
152757
+ const result = await this.twilioClient.makeCall({
152758
+ to: to3,
152759
+ from: fromNumber,
152760
+ url: `${webhookUrl}/api/v1/telephony/webhooks/voice`,
152761
+ statusCallback: `${webhookUrl}/api/v1/telephony/webhooks/voice-status`,
152762
+ record: this.config.voice?.recordCalls
152763
+ });
152764
+ if (!result.success) {
152765
+ return { success: false, message: `Failed to make call: ${result.error}` };
152766
+ }
152767
+ const callSid = result.data?.sid;
152768
+ const log2 = this.store.createCallLog({
152769
+ callSid,
152770
+ fromNumber,
152771
+ toNumber: to3,
152772
+ direction: "outbound",
152773
+ status: "pending",
152774
+ assistantId: this.assistantId
152775
+ });
152776
+ this.callManager.addCall({
152777
+ callSid,
152778
+ fromNumber,
152779
+ toNumber: to3,
152780
+ direction: "outbound",
152781
+ assistantId: this.assistantId
152782
+ });
152783
+ return {
152784
+ success: true,
152785
+ message: `Calling ${to3}...`,
152786
+ callSid,
152787
+ id: log2.id
152788
+ };
152789
+ }
152790
+ getCallHistory(options) {
152791
+ return this.store.listCallLogs({
152792
+ assistantId: this.assistantId,
152793
+ limit: options?.limit || 20
152794
+ });
152795
+ }
152796
+ getSmsHistory(options) {
152797
+ return this.store.listSmsLogs({
152798
+ assistantId: this.assistantId,
152799
+ messageType: options?.messageType,
152800
+ limit: options?.limit || 20
152801
+ });
152802
+ }
152803
+ listPhoneNumbers() {
152804
+ return this.store.listPhoneNumbers("active");
152805
+ }
152806
+ async syncPhoneNumbers() {
152807
+ if (!this.twilioClient) {
152808
+ return { success: false, message: "Twilio is not configured." };
152809
+ }
152810
+ const result = await this.twilioClient.listPhoneNumbers();
152811
+ if (!result.success) {
152812
+ return { success: false, message: `Failed to list numbers: ${result.error}` };
152813
+ }
152814
+ const numbers = result.data?.incoming_phone_numbers || [];
152815
+ let synced = 0;
152816
+ for (const num of numbers) {
152817
+ const phoneNumber = String(num.phone_number || "");
152818
+ const existing = this.store.getPhoneNumberByNumber(phoneNumber);
152819
+ if (!existing && phoneNumber) {
152820
+ this.store.addPhoneNumber(phoneNumber, num.friendly_name ? String(num.friendly_name) : null, num.sid ? String(num.sid) : null, {
152821
+ voice: Boolean(num.capabilities?.voice),
152822
+ sms: Boolean(num.capabilities?.sms)
152823
+ });
152824
+ synced++;
152825
+ }
152826
+ }
152827
+ return {
152828
+ success: true,
152829
+ message: `Synced ${synced} phone number${synced !== 1 ? "s" : ""} from Twilio.`
152830
+ };
152831
+ }
152832
+ listRoutingRules() {
152833
+ return this.store.listRoutingRules();
152834
+ }
152835
+ createRoutingRule(params) {
152836
+ const rule = this.store.createRoutingRule(params);
152837
+ return {
152838
+ success: true,
152839
+ message: `Routing rule "${rule.name}" created (priority ${rule.priority}).`,
152840
+ id: rule.id
152841
+ };
152842
+ }
152843
+ deleteRoutingRule(id) {
152844
+ const success = this.store.deleteRoutingRule(id);
152845
+ return {
152846
+ success,
152847
+ message: success ? "Routing rule deleted." : "Routing rule not found."
152848
+ };
152849
+ }
152850
+ getStatus() {
152851
+ const phoneNumbers = this.store.listPhoneNumbers("active");
152852
+ const recentCalls = this.store.listCallLogs({ limit: 100 });
152853
+ const recentMessages = this.store.listSmsLogs({ limit: 100 });
152854
+ const routingRules = this.store.listRoutingRules();
152855
+ return {
152856
+ enabled: this.config.enabled !== false,
152857
+ twilioConfigured: this.twilioClient?.isConfigured() ?? false,
152858
+ elevenLabsConfigured: this.voiceBridge?.isConfigured() ?? false,
152859
+ phoneNumbers: phoneNumbers.length,
152860
+ activeCalls: this.callManager.getActiveCallCount(),
152861
+ routingRules: routingRules.length,
152862
+ recentCalls: recentCalls.length,
152863
+ recentMessages: recentMessages.length
152864
+ };
152865
+ }
152866
+ getUnreadForInjection() {
152867
+ const injectionConfig = this.config.injection || {};
152868
+ if (injectionConfig.enabled === false) {
152869
+ return [];
152870
+ }
152871
+ const maxPerTurn = injectionConfig.maxPerTurn || 5;
152872
+ return this.store.getUnreadInboundSms(this.assistantId, maxPerTurn);
152873
+ }
152874
+ buildInjectionContext(messages) {
152875
+ if (messages.length === 0)
152876
+ return "";
152877
+ const lines = [];
152878
+ lines.push("## Incoming Telephony Messages");
152879
+ lines.push("");
152880
+ for (const msg of messages) {
152881
+ const type = msg.messageType === "whatsapp" ? "WhatsApp" : "SMS";
152882
+ const ago = formatTimeAgo2(msg.createdAt);
152883
+ lines.push(`**${type} from ${msg.fromNumber}** (${ago}):`);
152884
+ lines.push(msg.body);
152885
+ lines.push("");
152886
+ }
152887
+ lines.push("Use telephony_send_sms or telephony_send_whatsapp to reply.");
152888
+ return lines.join(`
152889
+ `);
152890
+ }
152891
+ markInjected(messages) {
152892
+ for (const msg of messages) {
152893
+ this.store.updateSmsStatus(msg.id, "delivered");
152894
+ }
152895
+ }
152896
+ getStore() {
152897
+ return this.store;
152898
+ }
152899
+ getTwilioClient() {
152900
+ return this.twilioClient;
152901
+ }
152902
+ getCallManager() {
152903
+ return this.callManager;
152904
+ }
152905
+ getVoiceBridge() {
152906
+ return this.voiceBridge;
152907
+ }
152908
+ getAssistantId() {
152909
+ return this.assistantId;
152910
+ }
152911
+ getAssistantName() {
152912
+ return this.assistantName;
152913
+ }
152914
+ getConfig() {
152915
+ return this.config;
152916
+ }
152917
+ cleanup() {
152918
+ const maxAgeDays = this.config.storage?.maxAgeDays || 90;
152919
+ const maxCallLogs = this.config.storage?.maxCallLogs || 1000;
152920
+ const maxSmsLogs = this.config.storage?.maxSmsLogs || 5000;
152921
+ this.callManager.cleanupStaleCalls();
152922
+ return this.store.cleanup(maxAgeDays, maxCallLogs, maxSmsLogs);
152923
+ }
152924
+ close() {
152925
+ this.callManager.endAllCalls();
152926
+ this.voiceBridge?.closeAll();
152927
+ this.store.close();
152928
+ }
152929
+ }
152930
+ function formatTimeAgo2(isoDate) {
152931
+ const now2 = Date.now();
152932
+ const then = new Date(isoDate).getTime();
152933
+ const diffMs = now2 - then;
152934
+ if (diffMs < 60000) {
152935
+ const secs = Math.floor(diffMs / 1000);
152936
+ return `${secs}s ago`;
152937
+ }
152938
+ if (diffMs < 3600000) {
152939
+ const mins = Math.floor(diffMs / 60000);
152940
+ return `${mins}m ago`;
152941
+ }
152942
+ if (diffMs < 86400000) {
152943
+ const hours = Math.floor(diffMs / 3600000);
152944
+ return `${hours}h ago`;
152945
+ }
152946
+ const days = Math.floor(diffMs / 86400000);
152947
+ return `${days}d ago`;
152948
+ }
152949
+ function createTelephonyManager(assistantId, assistantName, config) {
152950
+ return new TelephonyManager({
152951
+ assistantId,
152952
+ assistantName,
152953
+ config
152954
+ });
152955
+ }
152956
+ // ../core/src/telephony/tools.ts
152957
+ var telephonySendSmsTool = {
152958
+ name: "telephony_send_sms",
152959
+ description: "Send an SMS text message to a phone number.",
152960
+ parameters: {
152961
+ type: "object",
152962
+ properties: {
152963
+ to: {
152964
+ type: "string",
152965
+ description: 'Recipient phone number in E.164 format (e.g., "+15551234567")'
152966
+ },
152967
+ body: {
152968
+ type: "string",
152969
+ description: "Message content to send"
152970
+ },
152971
+ from: {
152972
+ type: "string",
152973
+ description: "Sender phone number (optional, uses default if not set)"
152974
+ }
152975
+ },
152976
+ required: ["to", "body"]
152977
+ }
152978
+ };
152979
+ var telephonySendWhatsappTool = {
152980
+ name: "telephony_send_whatsapp",
152981
+ description: "Send a WhatsApp message to a phone number.",
152982
+ parameters: {
152983
+ type: "object",
152984
+ properties: {
152985
+ to: {
152986
+ type: "string",
152987
+ description: 'Recipient phone number in E.164 format (e.g., "+15551234567")'
152988
+ },
152989
+ body: {
152990
+ type: "string",
152991
+ description: "Message content to send"
152992
+ },
152993
+ from: {
152994
+ type: "string",
152995
+ description: "Sender phone number (optional, uses default if not set)"
152996
+ }
152997
+ },
152998
+ required: ["to", "body"]
152999
+ }
153000
+ };
153001
+ var telephonyCallTool = {
153002
+ name: "telephony_call",
153003
+ description: "Initiate an outbound voice call. The call will be connected to the AI voice agent.",
153004
+ parameters: {
153005
+ type: "object",
153006
+ properties: {
153007
+ to: {
153008
+ type: "string",
153009
+ description: 'Phone number to call in E.164 format (e.g., "+15551234567")'
153010
+ },
153011
+ from: {
153012
+ type: "string",
153013
+ description: "Caller phone number (optional, uses default if not set)"
153014
+ }
153015
+ },
153016
+ required: ["to"]
153017
+ }
153018
+ };
153019
+ var telephonyCallHistoryTool = {
153020
+ name: "telephony_call_history",
153021
+ description: "Get recent call history. Returns call logs with status, duration, and timestamps.",
153022
+ parameters: {
153023
+ type: "object",
153024
+ properties: {
153025
+ limit: {
153026
+ type: "number",
153027
+ description: "Maximum number of calls to return (default: 20)"
153028
+ }
153029
+ },
153030
+ required: []
153031
+ }
153032
+ };
153033
+ var telephonySmsHistoryTool = {
153034
+ name: "telephony_sms_history",
153035
+ description: "Get recent SMS and WhatsApp message history.",
153036
+ parameters: {
153037
+ type: "object",
153038
+ properties: {
153039
+ limit: {
153040
+ type: "number",
153041
+ description: "Maximum messages to return (default: 20)"
153042
+ },
153043
+ type: {
153044
+ type: "string",
153045
+ enum: ["sms", "whatsapp"],
153046
+ description: "Filter by message type (default: all)"
153047
+ }
153048
+ },
153049
+ required: []
153050
+ }
153051
+ };
153052
+ var telephonyPhoneNumbersTool = {
153053
+ name: "telephony_phone_numbers",
153054
+ description: "List available phone numbers with their capabilities (voice, SMS, WhatsApp).",
153055
+ parameters: {
153056
+ type: "object",
153057
+ properties: {},
153058
+ required: []
153059
+ }
153060
+ };
153061
+ var telephonyRoutingRulesTool = {
153062
+ name: "telephony_routing_rules",
153063
+ description: "View and manage routing rules that direct incoming calls/messages to specific assistants.",
153064
+ parameters: {
153065
+ type: "object",
153066
+ properties: {
153067
+ action: {
153068
+ type: "string",
153069
+ enum: ["list", "create", "delete"],
153070
+ description: "Action to perform (default: list)"
153071
+ },
153072
+ name: {
153073
+ type: "string",
153074
+ description: "Rule name (for create)"
153075
+ },
153076
+ priority: {
153077
+ type: "number",
153078
+ description: "Priority (lower = higher priority, for create)"
153079
+ },
153080
+ from_pattern: {
153081
+ type: "string",
153082
+ description: 'From number pattern (e.g., "+1555*", for create)'
153083
+ },
153084
+ message_type: {
153085
+ type: "string",
153086
+ enum: ["sms", "whatsapp", "voice", "all"],
153087
+ description: "Message type filter (for create)"
153088
+ },
153089
+ rule_id: {
153090
+ type: "string",
153091
+ description: "Rule ID (for delete)"
153092
+ }
153093
+ },
153094
+ required: []
153095
+ }
153096
+ };
153097
+ var telephonyStatusTool = {
153098
+ name: "telephony_status",
153099
+ description: "Get telephony system status including configured numbers, active calls, and connection health.",
153100
+ parameters: {
153101
+ type: "object",
153102
+ properties: {},
153103
+ required: []
153104
+ }
153105
+ };
153106
+ function createTelephonyToolExecutors(getTelephonyManager) {
153107
+ return {
153108
+ telephony_send_sms: async (input) => {
153109
+ const manager = getTelephonyManager();
153110
+ if (!manager) {
153111
+ return "Error: Telephony is not enabled. Set telephony.enabled: true in config.";
153112
+ }
153113
+ const to3 = String(input.to || "").trim();
153114
+ const body = String(input.body || "").trim();
153115
+ if (!to3)
153116
+ return "Error: Recipient phone number (to) is required.";
153117
+ if (!body)
153118
+ return "Error: Message body is required.";
153119
+ const from = input.from ? String(input.from).trim() : undefined;
153120
+ const result = await manager.sendSms(to3, body, from);
153121
+ return result.success ? result.message : `Error: ${result.message}`;
153122
+ },
153123
+ telephony_send_whatsapp: async (input) => {
153124
+ const manager = getTelephonyManager();
153125
+ if (!manager) {
153126
+ return "Error: Telephony is not enabled. Set telephony.enabled: true in config.";
153127
+ }
153128
+ const to3 = String(input.to || "").trim();
153129
+ const body = String(input.body || "").trim();
153130
+ if (!to3)
153131
+ return "Error: Recipient phone number (to) is required.";
153132
+ if (!body)
153133
+ return "Error: Message body is required.";
153134
+ const from = input.from ? String(input.from).trim() : undefined;
153135
+ const result = await manager.sendWhatsApp(to3, body, from);
153136
+ return result.success ? result.message : `Error: ${result.message}`;
153137
+ },
153138
+ telephony_call: async (input) => {
153139
+ const manager = getTelephonyManager();
153140
+ if (!manager) {
153141
+ return "Error: Telephony is not enabled. Set telephony.enabled: true in config.";
153142
+ }
153143
+ const to3 = String(input.to || "").trim();
153144
+ if (!to3)
153145
+ return "Error: Phone number (to) is required.";
153146
+ const from = input.from ? String(input.from).trim() : undefined;
153147
+ const result = await manager.makeCall(to3, from);
153148
+ return result.success ? result.message : `Error: ${result.message}`;
153149
+ },
153150
+ telephony_call_history: async (input) => {
153151
+ const manager = getTelephonyManager();
153152
+ if (!manager) {
153153
+ return "Error: Telephony is not enabled.";
153154
+ }
153155
+ const limit2 = typeof input.limit === "number" ? input.limit : 20;
153156
+ const calls = manager.getCallHistory({ limit: limit2 });
153157
+ if (calls.length === 0) {
153158
+ return "No call history found.";
153159
+ }
153160
+ const lines = [];
153161
+ lines.push(`## Call History (${calls.length})`);
153162
+ lines.push("");
153163
+ for (const call of calls) {
153164
+ const dir = call.direction === "inbound" ? "IN" : "OUT";
153165
+ const duration = call.duration != null ? `${call.duration}s` : "-";
153166
+ const date = new Date(call.createdAt).toLocaleString();
153167
+ lines.push(`[${dir}] ${call.fromNumber} \u2192 ${call.toNumber} | ${call.status} | ${duration} | ${date}`);
153168
+ }
153169
+ return lines.join(`
153170
+ `);
153171
+ },
153172
+ telephony_sms_history: async (input) => {
153173
+ const manager = getTelephonyManager();
153174
+ if (!manager) {
153175
+ return "Error: Telephony is not enabled.";
153176
+ }
153177
+ const limit2 = typeof input.limit === "number" ? input.limit : 20;
153178
+ const messageType = input.type;
153179
+ const messages = manager.getSmsHistory({ limit: limit2, messageType });
153180
+ if (messages.length === 0) {
153181
+ return "No message history found.";
153182
+ }
153183
+ const lines = [];
153184
+ lines.push(`## Message History (${messages.length})`);
153185
+ lines.push("");
153186
+ for (const msg of messages) {
153187
+ const dir = msg.direction === "inbound" ? "IN" : "OUT";
153188
+ const type = msg.messageType === "whatsapp" ? "WA" : "SMS";
153189
+ const date = new Date(msg.createdAt).toLocaleString();
153190
+ lines.push(`[${dir}/${type}] ${msg.fromNumber} \u2192 ${msg.toNumber} | ${msg.status}`);
153191
+ lines.push(` ${msg.bodyPreview}`);
153192
+ lines.push(` ${date}`);
153193
+ lines.push("");
153194
+ }
153195
+ return lines.join(`
153196
+ `);
153197
+ },
153198
+ telephony_phone_numbers: async () => {
153199
+ const manager = getTelephonyManager();
153200
+ if (!manager) {
153201
+ return "Error: Telephony is not enabled.";
153202
+ }
153203
+ const numbers = manager.listPhoneNumbers();
153204
+ if (numbers.length === 0) {
153205
+ return "No phone numbers configured. Use /phone sync to import from Twilio.";
153206
+ }
153207
+ const lines = [];
153208
+ lines.push(`## Phone Numbers (${numbers.length})`);
153209
+ lines.push("");
153210
+ for (const num of numbers) {
153211
+ const caps = [];
153212
+ if (num.capabilities.voice)
153213
+ caps.push("voice");
153214
+ if (num.capabilities.sms)
153215
+ caps.push("sms");
153216
+ if (num.capabilities.whatsapp)
153217
+ caps.push("whatsapp");
153218
+ const name2 = num.friendlyName ? ` (${num.friendlyName})` : "";
153219
+ lines.push(` ${num.number}${name2} [${caps.join(", ")}]`);
153220
+ }
153221
+ return lines.join(`
153222
+ `);
153223
+ },
153224
+ telephony_routing_rules: async (input) => {
153225
+ const manager = getTelephonyManager();
153226
+ if (!manager) {
153227
+ return "Error: Telephony is not enabled.";
153228
+ }
153229
+ const action = String(input.action || "list");
153230
+ if (action === "list") {
153231
+ const rules = manager.listRoutingRules();
153232
+ if (rules.length === 0) {
153233
+ return "No routing rules configured.";
153234
+ }
153235
+ const lines = [];
153236
+ lines.push(`## Routing Rules (${rules.length})`);
153237
+ lines.push("");
153238
+ for (const rule of rules) {
153239
+ const enabled = rule.enabled ? "" : " [DISABLED]";
153240
+ lines.push(`**${rule.name}** (priority: ${rule.priority})${enabled}`);
153241
+ lines.push(` ID: ${rule.id}`);
153242
+ lines.push(` Type: ${rule.messageType} | Target: ${rule.targetAssistantName}`);
153243
+ if (rule.fromPattern)
153244
+ lines.push(` From: ${rule.fromPattern}`);
153245
+ if (rule.toPattern)
153246
+ lines.push(` To: ${rule.toPattern}`);
153247
+ if (rule.keyword)
153248
+ lines.push(` Keyword: ${rule.keyword}`);
153249
+ lines.push("");
153250
+ }
153251
+ return lines.join(`
153252
+ `);
153253
+ }
153254
+ if (action === "create") {
153255
+ const name2 = String(input.name || "").trim();
153256
+ if (!name2)
153257
+ return "Error: Rule name is required.";
153258
+ const result = manager.createRoutingRule({
153259
+ name: name2,
153260
+ priority: typeof input.priority === "number" ? input.priority : undefined,
153261
+ fromPattern: input.from_pattern ? String(input.from_pattern) : undefined,
153262
+ messageType: input.message_type,
153263
+ targetAssistantId: manager.getAssistantId(),
153264
+ targetAssistantName: manager.getAssistantName()
153265
+ });
153266
+ return result.success ? result.message : `Error: ${result.message}`;
153267
+ }
153268
+ if (action === "delete") {
153269
+ const ruleId = String(input.rule_id || "").trim();
153270
+ if (!ruleId)
153271
+ return "Error: Rule ID is required.";
153272
+ const result = manager.deleteRoutingRule(ruleId);
153273
+ return result.success ? result.message : `Error: ${result.message}`;
153274
+ }
153275
+ return `Unknown action: ${action}. Use 'list', 'create', or 'delete'.`;
153276
+ },
153277
+ telephony_status: async () => {
153278
+ const manager = getTelephonyManager();
153279
+ if (!manager) {
153280
+ return "Telephony is not enabled. Set telephony.enabled: true in config.";
153281
+ }
153282
+ const status = manager.getStatus();
153283
+ const lines = [];
153284
+ lines.push("## Telephony Status");
153285
+ lines.push("");
153286
+ lines.push(`Enabled: ${status.enabled ? "Yes" : "No"}`);
153287
+ lines.push(`Twilio configured: ${status.twilioConfigured ? "Yes" : "No (set TWILIO_ACCOUNT_SID + TWILIO_AUTH_TOKEN)"}`);
153288
+ lines.push(`ElevenLabs AI: ${status.elevenLabsConfigured ? "Yes" : "No (set ELEVENLABS_API_KEY + ELEVENLABS_AGENT_ID)"}`);
153289
+ lines.push(`Phone numbers: ${status.phoneNumbers}`);
153290
+ lines.push(`Active calls: ${status.activeCalls}`);
153291
+ lines.push(`Routing rules: ${status.routingRules}`);
153292
+ lines.push(`Recent calls: ${status.recentCalls}`);
153293
+ lines.push(`Recent messages: ${status.recentMessages}`);
153294
+ return lines.join(`
153295
+ `);
153296
+ }
153297
+ };
153298
+ }
153299
+ var telephonyTools = [
153300
+ telephonySendSmsTool,
153301
+ telephonySendWhatsappTool,
153302
+ telephonyCallTool,
153303
+ telephonyCallHistoryTool,
153304
+ telephonySmsHistoryTool,
153305
+ telephonyPhoneNumbersTool,
153306
+ telephonyRoutingRulesTool,
153307
+ telephonyStatusTool
153308
+ ];
153309
+ function registerTelephonyTools(registry2, getTelephonyManager) {
153310
+ const executors = createTelephonyToolExecutors(getTelephonyManager);
153311
+ for (const tool of telephonyTools) {
153312
+ registry2.register(tool, executors[tool.name]);
153313
+ }
153314
+ }
151364
153315
  // ../core/src/sessions/store.ts
151365
- import { join as join44 } from "path";
153316
+ import { join as join45 } from "path";
151366
153317
  import { homedir as homedir21 } from "os";
151367
- import { existsSync as existsSync27, mkdirSync as mkdirSync17, writeFileSync as writeFileSync14, readFileSync as readFileSync17, readdirSync as readdirSync9, unlinkSync as unlinkSync6 } from "fs";
153318
+ import { existsSync as existsSync28, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14, readFileSync as readFileSync17, readdirSync as readdirSync9, unlinkSync as unlinkSync6 } from "fs";
151368
153319
 
151369
153320
  class SessionStore {
151370
153321
  basePath;
151371
153322
  constructor(basePath) {
151372
153323
  const envHome = process.env.HOME || process.env.USERPROFILE || homedir21();
151373
- this.basePath = basePath || join44(envHome, ".assistants", "sessions");
153324
+ this.basePath = basePath || join45(envHome, ".assistants", "sessions");
151374
153325
  this.ensureDir();
151375
153326
  }
151376
153327
  ensureDir() {
151377
- if (!existsSync27(this.basePath)) {
151378
- mkdirSync17(this.basePath, { recursive: true });
153328
+ if (!existsSync28(this.basePath)) {
153329
+ mkdirSync18(this.basePath, { recursive: true });
151379
153330
  }
151380
153331
  }
151381
153332
  getSessionPath(id) {
151382
- return join44(this.basePath, `${id}.json`);
153333
+ return join45(this.basePath, `${id}.json`);
151383
153334
  }
151384
153335
  save(data) {
151385
153336
  try {
@@ -151390,7 +153341,7 @@ class SessionStore {
151390
153341
  load(id) {
151391
153342
  try {
151392
153343
  const filePath = this.getSessionPath(id);
151393
- if (!existsSync27(filePath))
153344
+ if (!existsSync28(filePath))
151394
153345
  return null;
151395
153346
  return JSON.parse(readFileSync17(filePath, "utf-8"));
151396
153347
  } catch {
@@ -151404,7 +153355,7 @@ class SessionStore {
151404
153355
  const sessions = [];
151405
153356
  for (const file of files) {
151406
153357
  try {
151407
- const data = JSON.parse(readFileSync17(join44(this.basePath, file), "utf-8"));
153358
+ const data = JSON.parse(readFileSync17(join45(this.basePath, file), "utf-8"));
151408
153359
  sessions.push(data);
151409
153360
  } catch {}
151410
153361
  }
@@ -151416,7 +153367,7 @@ class SessionStore {
151416
153367
  delete(id) {
151417
153368
  try {
151418
153369
  const filePath = this.getSessionPath(id);
151419
- if (existsSync27(filePath)) {
153370
+ if (existsSync28(filePath)) {
151420
153371
  unlinkSync6(filePath);
151421
153372
  }
151422
153373
  } catch {}
@@ -151733,6 +153684,12 @@ class EmbeddedClient {
151733
153684
  }
151734
153685
  return null;
151735
153686
  }
153687
+ getTelephonyManager() {
153688
+ if (typeof this.assistantLoop.getTelephonyManager === "function") {
153689
+ return this.assistantLoop.getTelephonyManager();
153690
+ }
153691
+ return null;
153692
+ }
151736
153693
  getWalletManager() {
151737
153694
  if (typeof this.assistantLoop.getWalletManager === "function") {
151738
153695
  return this.assistantLoop.getWalletManager();
@@ -153603,7 +155560,7 @@ function createSelfAwarenessToolExecutors(context) {
153603
155560
  workspace_map: async (input) => {
153604
155561
  const { readdir: readdir7, stat: stat5, access } = await import("fs/promises");
153605
155562
  const { execSync } = await import("child_process");
153606
- const { join: join45, basename: basename6 } = await import("path");
155563
+ const { join: join46, basename: basename6 } = await import("path");
153607
155564
  const depth = input.depth ?? 3;
153608
155565
  const includeGitStatus = input.include_git_status !== false;
153609
155566
  const includeRecentFiles = input.include_recent_files !== false;
@@ -153649,7 +155606,7 @@ function createSelfAwarenessToolExecutors(context) {
153649
155606
  break;
153650
155607
  }
153651
155608
  if (entry.isDirectory()) {
153652
- const children = await buildTree(join45(dir, entry.name), currentDepth + 1);
155609
+ const children = await buildTree(join46(dir, entry.name), currentDepth + 1);
153653
155610
  nodes.push({
153654
155611
  name: entry.name,
153655
155612
  type: "directory",
@@ -153675,7 +155632,7 @@ function createSelfAwarenessToolExecutors(context) {
153675
155632
  };
153676
155633
  if (includeGitStatus) {
153677
155634
  try {
153678
- await access(join45(cwd, ".git"));
155635
+ await access(join46(cwd, ".git"));
153679
155636
  gitStatus.isRepo = true;
153680
155637
  try {
153681
155638
  const branch = execSync("git branch --show-current", { cwd, encoding: "utf-8" }).trim();
@@ -153706,7 +155663,7 @@ function createSelfAwarenessToolExecutors(context) {
153706
155663
  for (const entry of entries) {
153707
155664
  if (shouldIgnore(entry.name))
153708
155665
  continue;
153709
- const fullPath = join45(dir, entry.name);
155666
+ const fullPath = join46(dir, entry.name);
153710
155667
  const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
153711
155668
  if (entry.isFile()) {
153712
155669
  try {
@@ -153740,12 +155697,12 @@ function createSelfAwarenessToolExecutors(context) {
153740
155697
  let projectName = basename6(cwd);
153741
155698
  for (const indicator of projectIndicators) {
153742
155699
  try {
153743
- await access(join45(cwd, indicator.file));
155700
+ await access(join46(cwd, indicator.file));
153744
155701
  projectType = indicator.type;
153745
155702
  if (indicator.file === "package.json") {
153746
155703
  try {
153747
155704
  const { readFile: readFile16 } = await import("fs/promises");
153748
- const pkg = JSON.parse(await readFile16(join45(cwd, indicator.file), "utf-8"));
155705
+ const pkg = JSON.parse(await readFile16(join46(cwd, indicator.file), "utf-8"));
153749
155706
  projectName = pkg.name || projectName;
153750
155707
  } catch {}
153751
155708
  }
@@ -156334,8 +158291,8 @@ await __promiseAll([
156334
158291
  init_config(),
156335
158292
  init_runtime()
156336
158293
  ]);
156337
- import { join as join45, dirname as dirname19 } from "path";
156338
- import { existsSync as existsSync28, mkdirSync as mkdirSync18 } from "fs";
158294
+ import { join as join46, dirname as dirname20 } from "path";
158295
+ import { existsSync as existsSync29, mkdirSync as mkdirSync19 } from "fs";
156339
158296
  var MAX_KEY_LENGTH2 = 256;
156340
158297
  var MAX_VALUE_SIZE = 65536;
156341
158298
  var MAX_SUMMARY_LENGTH2 = 500;
@@ -156348,10 +158305,10 @@ class GlobalMemoryManager {
156348
158305
  config;
156349
158306
  constructor(options = {}) {
156350
158307
  const baseDir = getConfigDir();
156351
- const path2 = options.dbPath || join45(baseDir, "memory.db");
156352
- const dir = dirname19(path2);
156353
- if (!existsSync28(dir)) {
156354
- mkdirSync18(dir, { recursive: true });
158308
+ const path2 = options.dbPath || join46(baseDir, "memory.db");
158309
+ const dir = dirname20(path2);
158310
+ if (!existsSync29(dir)) {
158311
+ mkdirSync19(dir, { recursive: true });
156355
158312
  }
156356
158313
  const runtime = getRuntime();
156357
158314
  this.db = runtime.openDatabase(path2);
@@ -157755,8 +159712,8 @@ function createCapabilityChain(scope, capabilities) {
157755
159712
  };
157756
159713
  }
157757
159714
  // ../core/src/capabilities/storage.ts
157758
- import { existsSync as existsSync29, mkdirSync as mkdirSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
157759
- import { join as join46, dirname as dirname20 } from "path";
159715
+ import { existsSync as existsSync30, mkdirSync as mkdirSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
159716
+ import { join as join47, dirname as dirname21 } from "path";
157760
159717
  import { homedir as homedir22 } from "os";
157761
159718
  var DEFAULT_STORAGE_CONFIG = {
157762
159719
  enabled: true,
@@ -157777,14 +159734,14 @@ class CapabilityStorage {
157777
159734
  return this.config.storagePath;
157778
159735
  }
157779
159736
  const home = process.env.HOME || process.env.USERPROFILE || homedir22();
157780
- return join46(home, ".assistants", "capabilities", "store.json");
159737
+ return join47(home, ".assistants", "capabilities", "store.json");
157781
159738
  }
157782
159739
  load() {
157783
159740
  if (!this.config.enabled)
157784
159741
  return;
157785
159742
  try {
157786
159743
  const path2 = this.getStoragePath();
157787
- if (!existsSync29(path2))
159744
+ if (!existsSync30(path2))
157788
159745
  return;
157789
159746
  const data = JSON.parse(readFileSync18(path2, "utf-8"));
157790
159747
  if (data.chains) {
@@ -157804,9 +159761,9 @@ class CapabilityStorage {
157804
159761
  return;
157805
159762
  try {
157806
159763
  const path2 = this.getStoragePath();
157807
- const dir = dirname20(path2);
157808
- if (!existsSync29(dir)) {
157809
- mkdirSync19(dir, { recursive: true });
159764
+ const dir = dirname21(path2);
159765
+ if (!existsSync30(dir)) {
159766
+ mkdirSync20(dir, { recursive: true });
157810
159767
  }
157811
159768
  const data = {
157812
159769
  version: 1,
@@ -158192,6 +160149,7 @@ class AssistantLoop {
158192
160149
  messagesManager = null;
158193
160150
  webhooksManager = null;
158194
160151
  channelsManager = null;
160152
+ telephonyManager = null;
158195
160153
  memoryManager = null;
158196
160154
  memoryInjector = null;
158197
160155
  contextInjector = null;
@@ -158201,6 +160159,7 @@ class AssistantLoop {
158201
160159
  pendingMessagesContext = null;
158202
160160
  pendingWebhooksContext = null;
158203
160161
  pendingChannelsContext = null;
160162
+ pendingTelephonyContext = null;
158204
160163
  pendingMemoryContext = null;
158205
160164
  identityContext = null;
158206
160165
  projectContext = null;
@@ -158430,6 +160389,13 @@ class AssistantLoop {
158430
160389
  this.channelsManager = createChannelsManager(assistantId, assistantName, this.config.channels);
158431
160390
  registerChannelTools(this.toolRegistry, () => this.channelsManager);
158432
160391
  }
160392
+ if (this.config?.telephony?.enabled) {
160393
+ const assistant = this.assistantManager?.getActive();
160394
+ const assistantId = assistant?.id || this.sessionId;
160395
+ const assistantName = assistant?.name || "assistant";
160396
+ this.telephonyManager = createTelephonyManager(assistantId, assistantName, this.config.telephony);
160397
+ registerTelephonyTools(this.toolRegistry, () => this.telephonyManager);
160398
+ }
158433
160399
  const memoryConfig = this.config?.memory;
158434
160400
  if (memoryConfig?.enabled !== false) {
158435
160401
  const assistant = this.assistantManager?.getActive();
@@ -158761,6 +160727,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
158761
160727
  await this.injectPendingMessages();
158762
160728
  await this.injectPendingWebhookEvents();
158763
160729
  await this.injectPendingChannelMessages();
160730
+ await this.injectPendingTelephonyMessages();
158764
160731
  await this.injectMemoryContext(userMessage);
158765
160732
  await this.injectContextInfo();
158766
160733
  } catch (error2) {
@@ -159479,6 +161446,7 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
159479
161446
  getMessagesManager: () => this.messagesManager,
159480
161447
  getWebhooksManager: () => this.webhooksManager,
159481
161448
  getChannelsManager: () => this.channelsManager,
161449
+ getTelephonyManager: () => this.telephonyManager,
159482
161450
  getMemoryManager: () => this.memoryManager,
159483
161451
  refreshIdentityContext: async () => {
159484
161452
  if (this.identityManager) {
@@ -159720,6 +161688,8 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
159720
161688
  this.webhooksManager?.stopWatching();
159721
161689
  this.channelsManager?.close();
159722
161690
  this.channelsManager = null;
161691
+ this.telephonyManager?.close();
161692
+ this.telephonyManager = null;
159723
161693
  this.memoryManager?.close();
159724
161694
  this.memoryManager = null;
159725
161695
  this.memoryInjector = null;
@@ -159797,6 +161767,9 @@ You are running in **autonomous mode**. You manage your own wakeup schedule.
159797
161767
  getChannelsManager() {
159798
161768
  return this.channelsManager;
159799
161769
  }
161770
+ getTelephonyManager() {
161771
+ return this.telephonyManager;
161772
+ }
159800
161773
  getWalletManager() {
159801
161774
  return this.walletManager;
159802
161775
  }
@@ -160029,7 +162002,7 @@ ${content.trim()}`);
160029
162002
  if (!heartbeatConfig)
160030
162003
  return;
160031
162004
  this.heartbeatRuntimeConfig = heartbeatConfig;
160032
- const statePath = join47(getConfigDir(), "state", `${this.sessionId}.json`);
162005
+ const statePath = join48(getConfigDir(), "state", `${this.sessionId}.json`);
160033
162006
  this.heartbeatManager = new HeartbeatManager(heartbeatConfig);
160034
162007
  this.heartbeatPersistence = new StatePersistence(statePath);
160035
162008
  this.heartbeatRecovery = new RecoveryManager(this.heartbeatPersistence, heartbeatConfig.persistPath, heartbeatConfig.staleThresholdMs, {
@@ -160150,7 +162123,7 @@ ${content.trim()}`);
160150
162123
  async startEnergySystem() {
160151
162124
  if (!this.config || this.config.energy?.enabled === false)
160152
162125
  return;
160153
- const statePath = join47(getConfigDir(), "energy", "state.json");
162126
+ const statePath = join48(getConfigDir(), "energy", "state.json");
160154
162127
  this.energyManager = new EnergyManager(this.config.energy, new EnergyStorage(statePath));
160155
162128
  await this.energyManager.initialize();
160156
162129
  this.refreshEnergyEffects();
@@ -160257,6 +162230,29 @@ ${effects.message}
160257
162230
  this.pendingChannelsContext = null;
160258
162231
  }
160259
162232
  }
162233
+ async injectPendingTelephonyMessages() {
162234
+ if (!this.telephonyManager)
162235
+ return;
162236
+ try {
162237
+ if (this.pendingTelephonyContext) {
162238
+ const previous = this.pendingTelephonyContext.trim();
162239
+ this.context.removeSystemMessages((content) => content.trim() === previous);
162240
+ this.pendingTelephonyContext = null;
162241
+ }
162242
+ const pending = this.telephonyManager.getUnreadForInjection();
162243
+ if (pending.length === 0) {
162244
+ return;
162245
+ }
162246
+ this.pendingTelephonyContext = this.telephonyManager.buildInjectionContext(pending);
162247
+ if (this.pendingTelephonyContext) {
162248
+ this.context.addSystemMessage(this.pendingTelephonyContext);
162249
+ }
162250
+ this.telephonyManager.markInjected(pending);
162251
+ } catch (error2) {
162252
+ console.error("Failed to inject pending telephony messages:", error2);
162253
+ this.pendingTelephonyContext = null;
162254
+ }
162255
+ }
160260
162256
  async injectMemoryContext(userMessage) {
160261
162257
  if (!this.memoryInjector || !this.memoryInjector.isEnabled())
160262
162258
  return;
@@ -160526,8 +162522,8 @@ ${this.identityContext}`);
160526
162522
  return null;
160527
162523
  const intervalMs = Math.max(1000, config.heartbeat?.intervalMs ?? 15000);
160528
162524
  const staleThresholdMs = Math.max(intervalMs * 2, config.heartbeat?.staleThresholdMs ?? 120000);
160529
- const persistPath = config.heartbeat?.persistPath ?? join47(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
160530
- const historyPath = config.heartbeat?.historyPath ?? join47(getConfigDir(), "heartbeats", "runs", `${this.sessionId}.jsonl`);
162525
+ const persistPath = config.heartbeat?.persistPath ?? join48(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
162526
+ const historyPath = config.heartbeat?.historyPath ?? join48(getConfigDir(), "heartbeats", "runs", `${this.sessionId}.jsonl`);
160531
162527
  return {
160532
162528
  intervalMs,
160533
162529
  staleThresholdMs,
@@ -160916,9 +162912,9 @@ class StatsTracker {
160916
162912
  }
160917
162913
  }
160918
162914
  // ../core/src/tools/connector-index.ts
160919
- import { join as join48, dirname as dirname21 } from "path";
162915
+ import { join as join49, dirname as dirname22 } from "path";
160920
162916
  import { homedir as homedir23 } from "os";
160921
- import { existsSync as existsSync30, mkdirSync as mkdirSync20, writeFileSync as writeFileSync16, readFileSync as readFileSync19 } from "fs";
162917
+ import { existsSync as existsSync31, mkdirSync as mkdirSync21, writeFileSync as writeFileSync16, readFileSync as readFileSync19 } from "fs";
160922
162918
  var TAG_KEYWORDS = {
160923
162919
  email: ["email", "mail", "inbox", "send", "receive", "message", "compose"],
160924
162920
  calendar: ["calendar", "event", "meeting", "schedule", "appointment"],
@@ -160955,13 +162951,13 @@ class ConnectorIndex {
160955
162951
  return envHome && envHome.trim().length > 0 ? envHome : homedir23();
160956
162952
  }
160957
162953
  getCachePath() {
160958
- return join48(this.getHomeDir(), ".assistants", "cache", "connector-index.json");
162954
+ return join49(this.getHomeDir(), ".assistants", "cache", "connector-index.json");
160959
162955
  }
160960
162956
  loadDiskCache() {
160961
162957
  ConnectorIndex.indexLoaded = true;
160962
162958
  try {
160963
162959
  const cachePath = this.getCachePath();
160964
- if (!existsSync30(cachePath))
162960
+ if (!existsSync31(cachePath))
160965
162961
  return;
160966
162962
  const data = JSON.parse(readFileSync19(cachePath, "utf-8"));
160967
162963
  if (data.version !== INDEX_VERSION)
@@ -160977,9 +162973,9 @@ class ConnectorIndex {
160977
162973
  saveDiskCache() {
160978
162974
  try {
160979
162975
  const cachePath = this.getCachePath();
160980
- const cacheDir = dirname21(cachePath);
160981
- if (!existsSync30(cacheDir)) {
160982
- mkdirSync20(cacheDir, { recursive: true });
162976
+ const cacheDir = dirname22(cachePath);
162977
+ if (!existsSync31(cacheDir)) {
162978
+ mkdirSync21(cacheDir, { recursive: true });
160983
162979
  }
160984
162980
  const data = {
160985
162981
  version: INDEX_VERSION,