@elizaos/plugin-phone 2.0.3-beta.2 → 2.0.3-beta.3

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 (123) hide show
  1. package/dist/companion/components/Chat.d.ts +14 -0
  2. package/dist/companion/components/Chat.d.ts.map +1 -0
  3. package/dist/companion/components/Chat.js +195 -0
  4. package/dist/companion/components/Chat.js.map +1 -0
  5. package/dist/companion/components/Pairing.d.ts +9 -0
  6. package/dist/companion/components/Pairing.d.ts.map +1 -0
  7. package/dist/companion/components/Pairing.js +175 -0
  8. package/dist/companion/components/Pairing.js.map +1 -0
  9. package/dist/companion/components/PhoneCompanionApp.d.ts +9 -0
  10. package/dist/companion/components/PhoneCompanionApp.d.ts.map +1 -0
  11. package/dist/companion/components/PhoneCompanionApp.js +174 -0
  12. package/dist/companion/components/PhoneCompanionApp.js.map +1 -0
  13. package/dist/companion/components/RemoteSession.d.ts +9 -0
  14. package/dist/companion/components/RemoteSession.d.ts.map +1 -0
  15. package/dist/companion/components/RemoteSession.js +382 -0
  16. package/dist/companion/components/RemoteSession.js.map +1 -0
  17. package/dist/companion/components/index.d.ts +5 -0
  18. package/dist/companion/components/index.d.ts.map +1 -0
  19. package/dist/companion/components/index.js +11 -0
  20. package/dist/companion/components/index.js.map +1 -0
  21. package/dist/companion/index.d.ts +14 -0
  22. package/dist/companion/index.d.ts.map +1 -0
  23. package/dist/companion/index.js +12 -0
  24. package/dist/companion/index.js.map +1 -0
  25. package/dist/companion/services/eliza-intent.d.ts +63 -0
  26. package/dist/companion/services/eliza-intent.d.ts.map +1 -0
  27. package/dist/companion/services/eliza-intent.js +52 -0
  28. package/dist/companion/services/eliza-intent.js.map +1 -0
  29. package/dist/companion/services/env.d.ts +11 -0
  30. package/dist/companion/services/env.d.ts.map +1 -0
  31. package/dist/companion/services/env.js +20 -0
  32. package/dist/companion/services/env.js.map +1 -0
  33. package/dist/companion/services/index.d.ts +14 -0
  34. package/dist/companion/services/index.d.ts.map +1 -0
  35. package/dist/companion/services/index.js +29 -0
  36. package/dist/companion/services/index.js.map +1 -0
  37. package/dist/companion/services/intent-bridge.d.ts +11 -0
  38. package/dist/companion/services/intent-bridge.d.ts.map +1 -0
  39. package/dist/companion/services/intent-bridge.js +15 -0
  40. package/dist/companion/services/intent-bridge.js.map +1 -0
  41. package/dist/companion/services/logger.d.ts +14 -0
  42. package/dist/companion/services/logger.d.ts.map +1 -0
  43. package/dist/companion/services/logger.js +50 -0
  44. package/dist/companion/services/logger.js.map +1 -0
  45. package/dist/companion/services/navigation.d.ts +17 -0
  46. package/dist/companion/services/navigation.d.ts.map +1 -0
  47. package/dist/companion/services/navigation.js +104 -0
  48. package/dist/companion/services/navigation.js.map +1 -0
  49. package/dist/companion/services/push.d.ts +27 -0
  50. package/dist/companion/services/push.d.ts.map +1 -0
  51. package/dist/companion/services/push.js +101 -0
  52. package/dist/companion/services/push.js.map +1 -0
  53. package/dist/companion/services/session-client.d.ts +114 -0
  54. package/dist/companion/services/session-client.d.ts.map +1 -0
  55. package/dist/companion/services/session-client.js +197 -0
  56. package/dist/companion/services/session-client.js.map +1 -0
  57. package/dist/components/PhoneAppView.d.ts +18 -0
  58. package/dist/components/PhoneAppView.d.ts.map +1 -0
  59. package/dist/components/PhoneAppView.helpers.d.ts +12 -0
  60. package/dist/components/PhoneAppView.helpers.d.ts.map +1 -0
  61. package/dist/components/PhoneAppView.helpers.js +44 -0
  62. package/dist/components/PhoneAppView.helpers.js.map +1 -0
  63. package/dist/components/PhoneAppView.interact.d.ts +2 -0
  64. package/dist/components/PhoneAppView.interact.d.ts.map +1 -0
  65. package/dist/components/PhoneAppView.interact.js +63 -0
  66. package/dist/components/PhoneAppView.interact.js.map +1 -0
  67. package/dist/components/PhoneAppView.js +566 -0
  68. package/dist/components/PhoneAppView.js.map +1 -0
  69. package/dist/components/PhoneSpatialView.d.ts +43 -0
  70. package/dist/components/PhoneSpatialView.d.ts.map +1 -0
  71. package/dist/components/PhoneSpatialView.js +180 -0
  72. package/dist/components/PhoneSpatialView.js.map +1 -0
  73. package/dist/components/PhoneView.d.ts +13 -0
  74. package/dist/components/PhoneView.d.ts.map +1 -0
  75. package/dist/components/PhoneView.js +161 -0
  76. package/dist/components/PhoneView.js.map +1 -0
  77. package/dist/components/phone-app.d.ts +13 -0
  78. package/dist/components/phone-app.d.ts.map +1 -0
  79. package/dist/components/phone-app.js +20 -0
  80. package/dist/components/phone-app.js.map +1 -0
  81. package/dist/components/phone-view-bundle.d.ts +3 -0
  82. package/dist/components/phone-view-bundle.d.ts.map +1 -0
  83. package/dist/components/phone-view-bundle.js +7 -0
  84. package/dist/components/phone-view-bundle.js.map +1 -0
  85. package/dist/index.d.ts +25 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +28 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/plugin.d.ts +17 -0
  90. package/dist/plugin.d.ts.map +1 -0
  91. package/dist/plugin.js +48 -0
  92. package/dist/plugin.js.map +1 -0
  93. package/dist/providers/call-log.d.ts +11 -0
  94. package/dist/providers/call-log.d.ts.map +1 -0
  95. package/dist/providers/call-log.js +66 -0
  96. package/dist/providers/call-log.js.map +1 -0
  97. package/dist/register-companion-page.d.ts +13 -0
  98. package/dist/register-companion-page.d.ts.map +1 -0
  99. package/dist/register-companion-page.js +12 -0
  100. package/dist/register-companion-page.js.map +1 -0
  101. package/dist/register-terminal-view.d.ts +15 -0
  102. package/dist/register-terminal-view.d.ts.map +1 -0
  103. package/dist/register-terminal-view.js +21 -0
  104. package/dist/register-terminal-view.js.map +1 -0
  105. package/dist/register.d.ts +9 -0
  106. package/dist/register.d.ts.map +1 -0
  107. package/dist/register.js +11 -0
  108. package/dist/register.js.map +1 -0
  109. package/dist/twilio.d.ts +33 -0
  110. package/dist/twilio.d.ts.map +1 -0
  111. package/dist/twilio.js +238 -0
  112. package/dist/twilio.js.map +1 -0
  113. package/dist/ui.d.ts +8 -0
  114. package/dist/ui.d.ts.map +1 -0
  115. package/dist/ui.js +23 -0
  116. package/dist/ui.js.map +1 -0
  117. package/dist/views/bundle.js +407 -0
  118. package/dist/views/bundle.js.map +1 -0
  119. package/dist/views/dist-Cd2YtKy4.js +270 -0
  120. package/dist/views/dist-Cd2YtKy4.js.map +1 -0
  121. package/dist/views/web-TGRkTsa8.js +58 -0
  122. package/dist/views/web-TGRkTsa8.js.map +1 -0
  123. package/package.json +9 -8
package/dist/plugin.js ADDED
@@ -0,0 +1,48 @@
1
+ import { phoneCallLogProvider } from "./providers/call-log.js";
2
+ const PHONE_APP_NAME = "@elizaos/plugin-phone";
3
+ const appPhonePlugin = {
4
+ name: PHONE_APP_NAME,
5
+ description: "Phone overlay: Android dialer + recent-calls context. Recent calls are surfaced read-only via the phoneCallLog provider. Outbound call placement routes through the canonical VOICE_CALL surface when a provider is wired. Also hosts the Phone Companion (Capacitor pairing + remote-session) surface.",
6
+ // VOICE_CALL is still host-adapted by plugin-personal-assistant. Keep this
7
+ // app plugin read-only until the Android dialer provider is wired.
8
+ actions: [],
9
+ providers: [phoneCallLogProvider],
10
+ views: [
11
+ // ONE declaration → GUI + XR + TUI, all drawn from the single PhoneView
12
+ // spatial source. `modalities` is a plain literal here (plugin.ts is not in
13
+ // the view bundle), so no brand-new `@elizaos/core` runtime export reaches
14
+ // the bundle build.
15
+ {
16
+ id: "phone",
17
+ label: "Phone",
18
+ description: "Android dialer and recent-calls log",
19
+ icon: "Phone",
20
+ path: "/phone",
21
+ modalities: ["gui", "xr", "tui"],
22
+ bundlePath: "dist/views/bundle.js",
23
+ componentExport: "PhoneView",
24
+ tags: ["phone", "calls", "android"],
25
+ visibleInManager: true,
26
+ desktopTabEnabled: true
27
+ }
28
+ ],
29
+ app: {
30
+ navTabs: [
31
+ {
32
+ id: "phone-companion",
33
+ label: "Phone Companion",
34
+ icon: "Smartphone",
35
+ path: "/phone-companion",
36
+ componentExport: "@elizaos/plugin-phone#PhoneCompanionApp"
37
+ }
38
+ ]
39
+ }
40
+ };
41
+ var plugin_default = appPhonePlugin;
42
+ import { phoneCallLogProvider as phoneCallLogProvider2 } from "./providers/call-log.js";
43
+ export {
44
+ appPhonePlugin,
45
+ plugin_default as default,
46
+ phoneCallLogProvider2 as phoneCallLogProvider
47
+ };
48
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * elizaOS runtime plugin for the Phone app: exposes a read-only phoneCallLog\n * provider for recent-calls context. Outbound calls are owned by the canonical\n * VOICE_CALL action; the Android dialer implementation remains internal until\n * it is wired as a VOICE_CALL provider. The agent\n * Android adapter applies hosted-app session gating when this package's\n * `/plugin` export is registered.\n *\n * Also declares the Phone Companion (Capacitor pairing/chat-mirror surface)\n * via `app.navTabs`, so the app shell can resolve and mount it dynamically\n * when the companion bundle runs alongside the desktop UI.\n */\n\nimport type { Plugin } from \"@elizaos/core\";\nimport { phoneCallLogProvider } from \"./providers/call-log.js\";\n\nconst PHONE_APP_NAME = \"@elizaos/plugin-phone\";\n\nexport const appPhonePlugin: Plugin = {\n name: PHONE_APP_NAME,\n description:\n \"Phone overlay: Android dialer + recent-calls context. Recent calls are \" +\n \"surfaced read-only via the phoneCallLog provider. Outbound call placement \" +\n \"routes through the canonical VOICE_CALL surface when a provider is wired. \" +\n \"Also hosts the Phone Companion (Capacitor pairing + remote-session) \" +\n \"surface.\",\n // VOICE_CALL is still host-adapted by plugin-personal-assistant. Keep this\n // app plugin read-only until the Android dialer provider is wired.\n actions: [],\n providers: [phoneCallLogProvider],\n views: [\n // ONE declaration → GUI + XR + TUI, all drawn from the single PhoneView\n // spatial source. `modalities` is a plain literal here (plugin.ts is not in\n // the view bundle), so no brand-new `@elizaos/core` runtime export reaches\n // the bundle build.\n {\n id: \"phone\",\n label: \"Phone\",\n description: \"Android dialer and recent-calls log\",\n icon: \"Phone\",\n path: \"/phone\",\n modalities: [\"gui\", \"xr\", \"tui\"],\n bundlePath: \"dist/views/bundle.js\",\n componentExport: \"PhoneView\",\n tags: [\"phone\", \"calls\", \"android\"],\n visibleInManager: true,\n desktopTabEnabled: true,\n },\n ],\n app: {\n navTabs: [\n {\n id: \"phone-companion\",\n label: \"Phone Companion\",\n icon: \"Smartphone\",\n path: \"/phone-companion\",\n componentExport: \"@elizaos/plugin-phone#PhoneCompanionApp\",\n },\n ],\n },\n};\n\nexport default appPhonePlugin;\n\nexport { phoneCallLogProvider } from \"./providers/call-log.js\";\n"],"mappings":"AAcA,SAAS,4BAA4B;AAErC,MAAM,iBAAiB;AAEhB,MAAM,iBAAyB;AAAA,EACpC,MAAM;AAAA,EACN,aACE;AAAA;AAAA;AAAA,EAOF,SAAS,CAAC;AAAA,EACV,WAAW,CAAC,oBAAoB;AAAA,EAChC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKL;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY,CAAC,OAAO,MAAM,KAAK;AAAA,MAC/B,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,MAAM,CAAC,SAAS,SAAS,SAAS;AAAA,MAClC,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,IACrB;AAAA,EACF;AAAA,EACA,KAAK;AAAA,IACH,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM;AAAA,QACN,MAAM;AAAA,QACN,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;AAEf,SAAS,wBAAAA,6BAA4B;","names":["phoneCallLogProvider"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * phoneCallLog provider — read-only Android call-history context.
3
+ *
4
+ * Reading the recent-calls list is state exposure, not an agent operation
5
+ * with side effects. Surfaced as a dynamic provider so the planner can pull
6
+ * call-log context when a question hinges on prior calls. Live operations
7
+ * such as outbound dialing route through the canonical VOICE_CALL surface.
8
+ */
9
+ import type { Provider } from "@elizaos/core";
10
+ export declare const phoneCallLogProvider: Provider;
11
+ //# sourceMappingURL=call-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-log.d.ts","sourceRoot":"","sources":["../../src/providers/call-log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAGV,QAAQ,EAGT,MAAM,eAAe,CAAC;AAcvB,eAAO,MAAM,oBAAoB,EAAE,QAkElC,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { Phone } from "@elizaos/capacitor-phone";
2
+ const CALL_LOG_LIMIT = 50;
3
+ const phoneCallLogProvider = {
4
+ name: "phoneCallLog",
5
+ description: "Read-only Android call history (number, cached name, timestamp, duration, call type) for resolving recent phone activity.",
6
+ descriptionCompressed: "Phone call log: number, name, date, duration, type.",
7
+ dynamic: true,
8
+ contexts: ["contacts", "messaging"],
9
+ contextGate: { anyOf: ["contacts", "messaging"] },
10
+ cacheScope: "turn",
11
+ roleGate: { minRole: "ADMIN" },
12
+ cacheStable: false,
13
+ get: async (_runtime, _message, _state) => {
14
+ try {
15
+ const { calls } = await Phone.listRecentCalls({ limit: CALL_LOG_LIMIT });
16
+ const entries = calls.map(
17
+ (call) => ({
18
+ id: call.id,
19
+ number: call.number,
20
+ cachedName: call.cachedName ?? "",
21
+ date: call.date,
22
+ durationSeconds: call.durationSeconds,
23
+ type: call.type,
24
+ isNew: call.isNew
25
+ })
26
+ );
27
+ return {
28
+ text: JSON.stringify({
29
+ phone_call_log: {
30
+ count: entries.length,
31
+ items: entries
32
+ }
33
+ }),
34
+ values: {
35
+ callLogAvailable: entries.length > 0,
36
+ callLogCount: entries.length
37
+ },
38
+ data: {
39
+ calls: entries,
40
+ count: entries.length,
41
+ limit: CALL_LOG_LIMIT
42
+ }
43
+ };
44
+ } catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error);
46
+ return {
47
+ text: "",
48
+ values: {
49
+ callLogAvailable: false,
50
+ callLogCount: 0,
51
+ callLogError: message
52
+ },
53
+ data: {
54
+ calls: [],
55
+ count: 0,
56
+ limit: CALL_LOG_LIMIT,
57
+ error: message
58
+ }
59
+ };
60
+ }
61
+ }
62
+ };
63
+ export {
64
+ phoneCallLogProvider
65
+ };
66
+ //# sourceMappingURL=call-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/providers/call-log.ts"],"sourcesContent":["/**\n * phoneCallLog provider — read-only Android call-history context.\n *\n * Reading the recent-calls list is state exposure, not an agent operation\n * with side effects. Surfaced as a dynamic provider so the planner can pull\n * call-log context when a question hinges on prior calls. Live operations\n * such as outbound dialing route through the canonical VOICE_CALL surface.\n */\n\nimport { type CallLogEntry, Phone } from \"@elizaos/capacitor-phone\";\nimport type {\n IAgentRuntime,\n Memory,\n Provider,\n ProviderResult,\n State,\n} from \"@elizaos/core\";\n\nconst CALL_LOG_LIMIT = 50;\n\ninterface PhoneCallLogEntry {\n id: string;\n number: string;\n cachedName: string;\n date: number;\n durationSeconds: number;\n type: string;\n isNew: boolean;\n}\n\nexport const phoneCallLogProvider: Provider = {\n name: \"phoneCallLog\",\n description:\n \"Read-only Android call history (number, cached name, timestamp, duration, call type) for resolving recent phone activity.\",\n descriptionCompressed: \"Phone call log: number, name, date, duration, type.\",\n dynamic: true,\n contexts: [\"contacts\", \"messaging\"],\n contextGate: { anyOf: [\"contacts\", \"messaging\"] },\n cacheScope: \"turn\",\n roleGate: { minRole: \"ADMIN\" },\n cacheStable: false,\n\n get: async (\n _runtime: IAgentRuntime,\n _message: Memory,\n _state: State,\n ): Promise<ProviderResult> => {\n try {\n const { calls } = await Phone.listRecentCalls({ limit: CALL_LOG_LIMIT });\n const entries: PhoneCallLogEntry[] = calls.map(\n (call: CallLogEntry): PhoneCallLogEntry => ({\n id: call.id,\n number: call.number,\n cachedName: call.cachedName ?? \"\",\n date: call.date,\n durationSeconds: call.durationSeconds,\n type: call.type,\n isNew: call.isNew,\n }),\n );\n\n return {\n text: JSON.stringify({\n phone_call_log: {\n count: entries.length,\n items: entries,\n },\n }),\n values: {\n callLogAvailable: entries.length > 0,\n callLogCount: entries.length,\n },\n data: {\n calls: entries,\n count: entries.length,\n limit: CALL_LOG_LIMIT,\n },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n text: \"\",\n values: {\n callLogAvailable: false,\n callLogCount: 0,\n callLogError: message,\n },\n data: {\n calls: [],\n count: 0,\n limit: CALL_LOG_LIMIT,\n error: message,\n },\n };\n }\n },\n};\n"],"mappings":"AASA,SAA4B,aAAa;AASzC,MAAM,iBAAiB;AAYhB,MAAM,uBAAiC;AAAA,EAC5C,MAAM;AAAA,EACN,aACE;AAAA,EACF,uBAAuB;AAAA,EACvB,SAAS;AAAA,EACT,UAAU,CAAC,YAAY,WAAW;AAAA,EAClC,aAAa,EAAE,OAAO,CAAC,YAAY,WAAW,EAAE;AAAA,EAChD,YAAY;AAAA,EACZ,UAAU,EAAE,SAAS,QAAQ;AAAA,EAC7B,aAAa;AAAA,EAEb,KAAK,OACH,UACA,UACA,WAC4B;AAC5B,QAAI;AACF,YAAM,EAAE,MAAM,IAAI,MAAM,MAAM,gBAAgB,EAAE,OAAO,eAAe,CAAC;AACvE,YAAM,UAA+B,MAAM;AAAA,QACzC,CAAC,UAA2C;AAAA,UAC1C,IAAI,KAAK;AAAA,UACT,QAAQ,KAAK;AAAA,UACb,YAAY,KAAK,cAAc;AAAA,UAC/B,MAAM,KAAK;AAAA,UACX,iBAAiB,KAAK;AAAA,UACtB,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,QACd;AAAA,MACF;AAEA,aAAO;AAAA,QACL,MAAM,KAAK,UAAU;AAAA,UACnB,gBAAgB;AAAA,YACd,OAAO,QAAQ;AAAA,YACf,OAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,QACD,QAAQ;AAAA,UACN,kBAAkB,QAAQ,SAAS;AAAA,UACnC,cAAc,QAAQ;AAAA,QACxB;AAAA,QACA,MAAM;AAAA,UACJ,OAAO;AAAA,UACP,OAAO,QAAQ;AAAA,UACf,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,kBAAkB;AAAA,UAClB,cAAc;AAAA,UACd,cAAc;AAAA,QAChB;AAAA,QACA,MAAM;AAAA,UACJ,OAAO,CAAC;AAAA,UACR,OAAO;AAAA,UACP,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Side-effect entry point — registers the Phone Companion page with the app
3
+ * shell's in-process page registry.
4
+ *
5
+ * The plugin manifest's `app.navTabs` declaration carries a `componentExport`
6
+ * specifier as a fallback for hosts that don't side-effect-import this file,
7
+ * while this registry entry keeps startup metadata lightweight and loads the
8
+ * companion surface only when the route is activated.
9
+ *
10
+ * Load this module once during app startup to register the page.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=register-companion-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-companion-page.d.ts","sourceRoot":"","sources":["../src/register-companion-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,12 @@
1
+ import { registerAppShellPage } from "@elizaos/ui/app-shell-registry";
2
+ registerAppShellPage({
3
+ id: "phone-companion",
4
+ pluginId: "@elizaos/plugin-phone",
5
+ label: "Phone Companion",
6
+ icon: "Smartphone",
7
+ path: "/phone-companion",
8
+ loader: () => import("./companion/components/PhoneCompanionApp.js").then((module) => ({
9
+ default: module.PhoneCompanionApp
10
+ }))
11
+ });
12
+ //# sourceMappingURL=register-companion-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/register-companion-page.ts"],"sourcesContent":["/**\n * Side-effect entry point — registers the Phone Companion page with the app\n * shell's in-process page registry.\n *\n * The plugin manifest's `app.navTabs` declaration carries a `componentExport`\n * specifier as a fallback for hosts that don't side-effect-import this file,\n * while this registry entry keeps startup metadata lightweight and loads the\n * companion surface only when the route is activated.\n *\n * Load this module once during app startup to register the page.\n */\n\nimport { registerAppShellPage } from \"@elizaos/ui/app-shell-registry\";\n\nregisterAppShellPage({\n id: \"phone-companion\",\n pluginId: \"@elizaos/plugin-phone\",\n label: \"Phone Companion\",\n icon: \"Smartphone\",\n path: \"/phone-companion\",\n loader: () =>\n import(\"./companion/components/PhoneCompanionApp.js\").then((module) => ({\n default: module.PhoneCompanionApp,\n })),\n});\n"],"mappings":"AAYA,SAAS,4BAA4B;AAErC,qBAAqB;AAAA,EACnB,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ,MACN,OAAO,6CAA6C,EAAE,KAAK,CAAC,YAAY;AAAA,IACtE,SAAS,OAAO;AAAA,EAClB,EAAE;AACN,CAAC;","names":[]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Register the phone view for terminal rendering.
3
+ *
4
+ * The agent terminal mounts plugin views by id from the `@elizaos/tui` terminal
5
+ * registry. This makes the phone's `viewType: "tui"` declaration render for real
6
+ * in the terminal (the unified {@link PhoneSpatialView}) rather than only
7
+ * navigating a GUI shell. A module-level snapshot lets a host push live call
8
+ * data; on a non-Android agent it defaults to the dialer with no recent calls.
9
+ */
10
+ import { type PhoneSnapshot } from "./components/PhoneSpatialView.tsx";
11
+ /** Update the snapshot the registered terminal view renders from. */
12
+ export declare function setPhoneTerminalSnapshot(next: PhoneSnapshot): void;
13
+ /** Register the phone terminal view; returns an unregister function. */
14
+ export declare function registerPhoneTerminalView(): () => void;
15
+ //# sourceMappingURL=register-terminal-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-terminal-view.d.ts","sourceRoot":"","sources":["../src/register-terminal-view.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,mCAAmC,CAAC;AAK3C,qEAAqE;AACrE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAElE;AAED,wEAAwE;AACxE,wBAAgB,yBAAyB,IAAI,MAAM,IAAI,CAItD"}
@@ -0,0 +1,21 @@
1
+ import { registerSpatialTerminalView } from "@elizaos/ui/spatial/tui";
2
+ import { createElement } from "react";
3
+ import {
4
+ PhoneSpatialView
5
+ } from "./components/PhoneSpatialView.js";
6
+ const EMPTY = { callReady: false, dialed: "", calls: [] };
7
+ let current = EMPTY;
8
+ function setPhoneTerminalSnapshot(next) {
9
+ current = next;
10
+ }
11
+ function registerPhoneTerminalView() {
12
+ return registerSpatialTerminalView(
13
+ "phone",
14
+ () => createElement(PhoneSpatialView, { snapshot: current })
15
+ );
16
+ }
17
+ export {
18
+ registerPhoneTerminalView,
19
+ setPhoneTerminalSnapshot
20
+ };
21
+ //# sourceMappingURL=register-terminal-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/register-terminal-view.tsx"],"sourcesContent":["/**\n * Register the phone view for terminal rendering.\n *\n * The agent terminal mounts plugin views by id from the `@elizaos/tui` terminal\n * registry. This makes the phone's `viewType: \"tui\"` declaration render for real\n * in the terminal (the unified {@link PhoneSpatialView}) rather than only\n * navigating a GUI shell. A module-level snapshot lets a host push live call\n * data; on a non-Android agent it defaults to the dialer with no recent calls.\n */\n\nimport { registerSpatialTerminalView } from \"@elizaos/ui/spatial/tui\";\nimport { createElement } from \"react\";\nimport {\n type PhoneSnapshot,\n PhoneSpatialView,\n} from \"./components/PhoneSpatialView.js\";\n\nconst EMPTY: PhoneSnapshot = { callReady: false, dialed: \"\", calls: [] };\nlet current: PhoneSnapshot = EMPTY;\n\n/** Update the snapshot the registered terminal view renders from. */\nexport function setPhoneTerminalSnapshot(next: PhoneSnapshot): void {\n current = next;\n}\n\n/** Register the phone terminal view; returns an unregister function. */\nexport function registerPhoneTerminalView(): () => void {\n return registerSpatialTerminalView(\"phone\", () =>\n createElement(PhoneSpatialView, { snapshot: current }),\n );\n}\n"],"mappings":"AAUA,SAAS,mCAAmC;AAC5C,SAAS,qBAAqB;AAC9B;AAAA,EAEE;AAAA,OACK;AAEP,MAAM,QAAuB,EAAE,WAAW,OAAO,QAAQ,IAAI,OAAO,CAAC,EAAE;AACvE,IAAI,UAAyB;AAGtB,SAAS,yBAAyB,MAA2B;AAClE,YAAU;AACZ;AAGO,SAAS,4BAAwC;AACtD,SAAO;AAAA,IAA4B;AAAA,IAAS,MAC1C,cAAc,kBAAkB,EAAE,UAAU,QAAQ,CAAC;AAAA,EACvD;AACF;","names":[]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Side-effect entry point for bundled phone surfaces.
3
+ *
4
+ * The Phone Companion is an app-shell page and must register on every host
5
+ * where the app shell can route to `/phone-companion`. The Android overlay app
6
+ * still only registers on ElizaOS.
7
+ */
8
+ import "./register-companion-page";
9
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,2BAA2B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { isElizaOS } from "@elizaos/ui";
2
+ import { registerPhoneApp } from "./components/phone-app.js";
3
+ import "./register-companion-page.js";
4
+ if (isElizaOS()) {
5
+ registerPhoneApp();
6
+ }
7
+ if (typeof window === "undefined") {
8
+ void import("./register-terminal-view.js").then((m) => m.registerPhoneTerminalView()).catch(() => {
9
+ });
10
+ }
11
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/register.ts"],"sourcesContent":["/**\n * Side-effect entry point for bundled phone surfaces.\n *\n * The Phone Companion is an app-shell page and must register on every host\n * where the app shell can route to `/phone-companion`. The Android overlay app\n * still only registers on ElizaOS.\n */\n\nimport { isElizaOS } from \"@elizaos/ui\";\nimport { registerPhoneApp } from \"./components/phone-app.js\";\nimport \"./register-companion-page.js\";\n\nif (isElizaOS()) {\n registerPhoneApp();\n}\n\n// In a terminal host (the Node agent, no DOM), register the phone view so it\n// renders inline in the terminal. Lazy + DOM-guarded so the terminal engine\n// stays out of browser/mobile bundles.\nif (typeof window === \"undefined\") {\n void import(\"./register-terminal-view.js\")\n .then((m) => m.registerPhoneTerminalView())\n .catch(() => {\n // Terminal rendering is best-effort; never block plugin load.\n });\n}\n"],"mappings":"AAQA,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AACjC,OAAO;AAEP,IAAI,UAAU,GAAG;AACf,mBAAiB;AACnB;AAKA,IAAI,OAAO,WAAW,aAAa;AACjC,OAAK,OAAO,6BAA6B,EACtC,KAAK,CAAC,MAAM,EAAE,0BAA0B,CAAC,EACzC,MAAM,MAAM;AAAA,EAEb,CAAC;AACL;","names":[]}
@@ -0,0 +1,33 @@
1
+ export interface TwilioCredentials {
2
+ accountSid: string;
3
+ authToken: string;
4
+ fromPhoneNumber: string;
5
+ }
6
+ export interface TwilioSmsBillingBreakdown {
7
+ segments: number;
8
+ rawCost: number;
9
+ markup: number;
10
+ billedCost: number;
11
+ markupRate: number;
12
+ costPerSegment: number;
13
+ }
14
+ export interface TwilioDeliveryResult {
15
+ ok: boolean;
16
+ status: number | null;
17
+ sid?: string;
18
+ error?: string;
19
+ retryCount?: number;
20
+ billing?: TwilioSmsBillingBreakdown;
21
+ }
22
+ export declare function readTwilioCredentialsFromEnv(env?: NodeJS.ProcessEnv): TwilioCredentials | null;
23
+ export declare function sendTwilioSms(args: {
24
+ credentials: TwilioCredentials;
25
+ to: string;
26
+ body: string;
27
+ }): Promise<TwilioDeliveryResult>;
28
+ export declare function sendTwilioVoiceCall(args: {
29
+ credentials: TwilioCredentials;
30
+ to: string;
31
+ message: string;
32
+ }): Promise<TwilioDeliveryResult>;
33
+ //# sourceMappingURL=twilio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../src/twilio.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACrC;AA8DD,wBAAgB,4BAA4B,CAC1C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,iBAAiB,GAAG,IAAI,CAY1B;AA4JD,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4BhC;AAWD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,WAAW,EAAE,iBAAiB,CAAC;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmBhC"}
package/dist/twilio.js ADDED
@@ -0,0 +1,238 @@
1
+ import { logger } from "@elizaos/core";
2
+ const TWILIO_SMS_MARKUP_RATE = 0.2;
3
+ const DEFAULT_SMS_COST_PER_SEGMENT_USD = 75e-4;
4
+ const MAX_RETRIES = 2;
5
+ const BASE_DELAY_MS = 1e3;
6
+ function createTwilioTelemetrySpan() {
7
+ return {
8
+ success: () => void 0,
9
+ failure: () => void 0
10
+ };
11
+ }
12
+ function roundCurrency(value) {
13
+ return Math.round(value * 100) / 100;
14
+ }
15
+ function calculateTwilioSmsBilling(body, costPerSegmentUsd) {
16
+ const segments = Math.max(1, Math.ceil(body.length / 160));
17
+ const rawCost = roundCurrency(segments * costPerSegmentUsd);
18
+ const markup = roundCurrency(rawCost * TWILIO_SMS_MARKUP_RATE);
19
+ return {
20
+ segments,
21
+ rawCost,
22
+ markup,
23
+ billedCost: roundCurrency(rawCost + markup),
24
+ markupRate: TWILIO_SMS_MARKUP_RATE,
25
+ costPerSegment: costPerSegmentUsd
26
+ };
27
+ }
28
+ function encodeBasicAuth(accountSid, authToken) {
29
+ return Buffer.from(`${accountSid}:${authToken}`).toString("base64");
30
+ }
31
+ function twilioOperation(path) {
32
+ return path.includes("/Calls.") ? "twilio_voice" : "twilio_sms";
33
+ }
34
+ function resolveSmsCostPerSegment() {
35
+ const raw = process.env.TWILIO_SMS_COST_PER_SEGMENT_USD;
36
+ if (!raw) return DEFAULT_SMS_COST_PER_SEGMENT_USD;
37
+ const parsed = Number.parseFloat(raw);
38
+ if (!Number.isFinite(parsed) || parsed < 0) {
39
+ logger.warn(
40
+ { raw },
41
+ "[phone] Invalid TWILIO_SMS_COST_PER_SEGMENT_USD; falling back to default"
42
+ );
43
+ return DEFAULT_SMS_COST_PER_SEGMENT_USD;
44
+ }
45
+ return parsed;
46
+ }
47
+ function readTwilioCredentialsFromEnv(env = process.env) {
48
+ const accountSid = env.TWILIO_ACCOUNT_SID?.trim();
49
+ const authToken = env.TWILIO_AUTH_TOKEN?.trim();
50
+ const fromPhoneNumber = env.TWILIO_PHONE_NUMBER?.trim();
51
+ if (!accountSid || !authToken || !fromPhoneNumber) {
52
+ return null;
53
+ }
54
+ return {
55
+ accountSid,
56
+ authToken,
57
+ fromPhoneNumber
58
+ };
59
+ }
60
+ function getTwilioBaseUrl() {
61
+ return process.env.ELIZA_MOCK_TWILIO_BASE ?? "https://api.twilio.com";
62
+ }
63
+ function isTransientFailure(result) {
64
+ if (result.status !== null && result.status >= 400 && result.status < 500) {
65
+ return false;
66
+ }
67
+ return true;
68
+ }
69
+ function validationFailure(error) {
70
+ return {
71
+ ok: false,
72
+ status: null,
73
+ error,
74
+ retryCount: 0
75
+ };
76
+ }
77
+ function nonEmptyTrimmed(value, field) {
78
+ const trimmed = value.trim();
79
+ if (trimmed.length === 0) {
80
+ return `${field} must be a non-empty string`;
81
+ }
82
+ return null;
83
+ }
84
+ function validateTwilioRequestInputs(args) {
85
+ return nonEmptyTrimmed(args.credentials.accountSid, "credentials.accountSid") ?? nonEmptyTrimmed(args.credentials.authToken, "credentials.authToken") ?? nonEmptyTrimmed(
86
+ args.credentials.fromPhoneNumber,
87
+ "credentials.fromPhoneNumber"
88
+ ) ?? nonEmptyTrimmed(args.to, "to") ?? nonEmptyTrimmed(args.message, args.messageField);
89
+ }
90
+ async function sendTwilioRequest(args) {
91
+ const { credentials, path, payload } = args;
92
+ const url = `${getTwilioBaseUrl()}/2010-04-01/Accounts/${encodeURIComponent(
93
+ credentials.accountSid
94
+ )}${path}`;
95
+ const operation = twilioOperation(path);
96
+ let lastResult = null;
97
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
98
+ if (attempt > 0) {
99
+ const delayMs = BASE_DELAY_MS * 2 ** (attempt - 1);
100
+ logger.warn(
101
+ {
102
+ boundary: "plugin-phone",
103
+ integration: "twilio",
104
+ operation,
105
+ attempt,
106
+ delayMs
107
+ },
108
+ `[phone] Twilio request retry ${attempt}/${MAX_RETRIES} after ${delayMs}ms`
109
+ );
110
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
111
+ }
112
+ const span = createTwilioTelemetrySpan();
113
+ try {
114
+ const response = await fetch(url, {
115
+ method: "POST",
116
+ headers: {
117
+ Authorization: `Basic ${encodeBasicAuth(
118
+ credentials.accountSid,
119
+ credentials.authToken
120
+ )}`,
121
+ "Content-Type": "application/x-www-form-urlencoded"
122
+ },
123
+ body: payload.toString(),
124
+ signal: AbortSignal.timeout(12e3)
125
+ });
126
+ const data = await response.json().catch(() => ({}));
127
+ if (!response.ok) {
128
+ const errorMsg = data.message ?? `HTTP ${response.status}`;
129
+ logger.warn(
130
+ {
131
+ boundary: "plugin-phone",
132
+ integration: "twilio",
133
+ operation,
134
+ statusCode: response.status
135
+ },
136
+ `[phone] Twilio request failed: ${errorMsg}`
137
+ );
138
+ span.failure({
139
+ statusCode: response.status,
140
+ errorKind: "http_error"
141
+ });
142
+ lastResult = {
143
+ ok: false,
144
+ status: response.status,
145
+ error: errorMsg,
146
+ retryCount: attempt
147
+ };
148
+ if (!isTransientFailure(lastResult)) {
149
+ return lastResult;
150
+ }
151
+ continue;
152
+ }
153
+ span.success({ statusCode: response.status });
154
+ return {
155
+ ok: true,
156
+ status: response.status,
157
+ sid: data.sid,
158
+ retryCount: attempt
159
+ };
160
+ } catch (error) {
161
+ const errorMsg = error instanceof Error ? error.message : String(error);
162
+ logger.error(
163
+ {
164
+ boundary: "plugin-phone",
165
+ integration: "twilio",
166
+ operation,
167
+ err: error instanceof Error ? error : void 0
168
+ },
169
+ `[phone] Twilio request failed: ${errorMsg}`
170
+ );
171
+ span.failure({
172
+ error,
173
+ errorKind: "network_error"
174
+ });
175
+ lastResult = {
176
+ ok: false,
177
+ status: null,
178
+ error: errorMsg,
179
+ retryCount: attempt
180
+ };
181
+ }
182
+ }
183
+ return lastResult;
184
+ }
185
+ async function sendTwilioSms(args) {
186
+ const { credentials, to, body } = args;
187
+ const validationError = validateTwilioRequestInputs({
188
+ credentials,
189
+ to,
190
+ messageField: "body",
191
+ message: body
192
+ });
193
+ if (validationError) return validationFailure(validationError);
194
+ const result = await sendTwilioRequest({
195
+ credentials,
196
+ path: "/Messages.json",
197
+ payload: new URLSearchParams({
198
+ To: to,
199
+ From: credentials.fromPhoneNumber,
200
+ Body: body
201
+ })
202
+ });
203
+ if (!result.ok) {
204
+ return result;
205
+ }
206
+ return {
207
+ ...result,
208
+ billing: calculateTwilioSmsBilling(body, resolveSmsCostPerSegment())
209
+ };
210
+ }
211
+ function escapeXml(text) {
212
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
213
+ }
214
+ async function sendTwilioVoiceCall(args) {
215
+ const { credentials, to, message } = args;
216
+ const validationError = validateTwilioRequestInputs({
217
+ credentials,
218
+ to,
219
+ messageField: "message",
220
+ message
221
+ });
222
+ if (validationError) return validationFailure(validationError);
223
+ return sendTwilioRequest({
224
+ credentials,
225
+ path: "/Calls.json",
226
+ payload: new URLSearchParams({
227
+ To: to,
228
+ From: credentials.fromPhoneNumber,
229
+ Twiml: `<Response><Say>${escapeXml(message)}</Say></Response>`
230
+ })
231
+ });
232
+ }
233
+ export {
234
+ readTwilioCredentialsFromEnv,
235
+ sendTwilioSms,
236
+ sendTwilioVoiceCall
237
+ };
238
+ //# sourceMappingURL=twilio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/twilio.ts"],"sourcesContent":["import { logger } from \"@elizaos/core\";\n\nexport interface TwilioCredentials {\n accountSid: string;\n authToken: string;\n fromPhoneNumber: string;\n}\n\nexport interface TwilioSmsBillingBreakdown {\n segments: number;\n rawCost: number;\n markup: number;\n billedCost: number;\n markupRate: number;\n costPerSegment: number;\n}\n\nexport interface TwilioDeliveryResult {\n ok: boolean;\n status: number | null;\n sid?: string;\n error?: string;\n retryCount?: number;\n billing?: TwilioSmsBillingBreakdown;\n}\n\ntype TwilioTelemetrySpan = {\n success: (metadata?: Record<string, unknown>) => void;\n failure: (metadata?: Record<string, unknown>) => void;\n};\n\nconst TWILIO_SMS_MARKUP_RATE = 0.2;\nconst DEFAULT_SMS_COST_PER_SEGMENT_USD = 0.0075;\nconst MAX_RETRIES = 2;\nconst BASE_DELAY_MS = 1_000;\n\nfunction createTwilioTelemetrySpan(): TwilioTelemetrySpan {\n return {\n success: () => undefined,\n failure: () => undefined,\n };\n}\n\nfunction roundCurrency(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\nfunction calculateTwilioSmsBilling(\n body: string,\n costPerSegmentUsd: number,\n): TwilioSmsBillingBreakdown {\n const segments = Math.max(1, Math.ceil(body.length / 160));\n const rawCost = roundCurrency(segments * costPerSegmentUsd);\n const markup = roundCurrency(rawCost * TWILIO_SMS_MARKUP_RATE);\n return {\n segments,\n rawCost,\n markup,\n billedCost: roundCurrency(rawCost + markup),\n markupRate: TWILIO_SMS_MARKUP_RATE,\n costPerSegment: costPerSegmentUsd,\n };\n}\n\nfunction encodeBasicAuth(accountSid: string, authToken: string): string {\n return Buffer.from(`${accountSid}:${authToken}`).toString(\"base64\");\n}\n\nfunction twilioOperation(path: string): string {\n return path.includes(\"/Calls.\") ? \"twilio_voice\" : \"twilio_sms\";\n}\n\nfunction resolveSmsCostPerSegment(): number {\n const raw = process.env.TWILIO_SMS_COST_PER_SEGMENT_USD;\n if (!raw) return DEFAULT_SMS_COST_PER_SEGMENT_USD;\n const parsed = Number.parseFloat(raw);\n if (!Number.isFinite(parsed) || parsed < 0) {\n logger.warn(\n { raw },\n \"[phone] Invalid TWILIO_SMS_COST_PER_SEGMENT_USD; falling back to default\",\n );\n return DEFAULT_SMS_COST_PER_SEGMENT_USD;\n }\n return parsed;\n}\n\nexport function readTwilioCredentialsFromEnv(\n env: NodeJS.ProcessEnv = process.env,\n): TwilioCredentials | null {\n const accountSid = env.TWILIO_ACCOUNT_SID?.trim();\n const authToken = env.TWILIO_AUTH_TOKEN?.trim();\n const fromPhoneNumber = env.TWILIO_PHONE_NUMBER?.trim();\n if (!accountSid || !authToken || !fromPhoneNumber) {\n return null;\n }\n return {\n accountSid,\n authToken,\n fromPhoneNumber,\n };\n}\n\nfunction getTwilioBaseUrl(): string {\n return process.env.ELIZA_MOCK_TWILIO_BASE ?? \"https://api.twilio.com\";\n}\n\nfunction isTransientFailure(result: TwilioDeliveryResult): boolean {\n if (result.status !== null && result.status >= 400 && result.status < 500) {\n return false;\n }\n return true;\n}\n\nfunction validationFailure(error: string): TwilioDeliveryResult {\n return {\n ok: false,\n status: null,\n error,\n retryCount: 0,\n };\n}\n\nfunction nonEmptyTrimmed(value: string, field: string): string | null {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n return `${field} must be a non-empty string`;\n }\n return null;\n}\n\nfunction validateTwilioRequestInputs(args: {\n credentials: TwilioCredentials;\n to: string;\n messageField: \"body\" | \"message\";\n message: string;\n}): string | null {\n return (\n nonEmptyTrimmed(args.credentials.accountSid, \"credentials.accountSid\") ??\n nonEmptyTrimmed(args.credentials.authToken, \"credentials.authToken\") ??\n nonEmptyTrimmed(\n args.credentials.fromPhoneNumber,\n \"credentials.fromPhoneNumber\",\n ) ??\n nonEmptyTrimmed(args.to, \"to\") ??\n nonEmptyTrimmed(args.message, args.messageField)\n );\n}\n\nasync function sendTwilioRequest(args: {\n credentials: TwilioCredentials;\n path: string;\n payload: URLSearchParams;\n}): Promise<TwilioDeliveryResult> {\n const { credentials, path, payload } = args;\n const url = `${getTwilioBaseUrl()}/2010-04-01/Accounts/${encodeURIComponent(\n credentials.accountSid,\n )}${path}`;\n const operation = twilioOperation(path);\n let lastResult: TwilioDeliveryResult | null = null;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n if (attempt > 0) {\n const delayMs = BASE_DELAY_MS * 2 ** (attempt - 1);\n logger.warn(\n {\n boundary: \"plugin-phone\",\n integration: \"twilio\",\n operation,\n attempt,\n delayMs,\n },\n `[phone] Twilio request retry ${attempt}/${MAX_RETRIES} after ${delayMs}ms`,\n );\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n const span = createTwilioTelemetrySpan();\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Basic ${encodeBasicAuth(\n credentials.accountSid,\n credentials.authToken,\n )}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: payload.toString(),\n signal: AbortSignal.timeout(12_000),\n });\n const data = (await response.json().catch(() => ({}))) as {\n sid?: string;\n message?: string;\n code?: number;\n };\n if (!response.ok) {\n const errorMsg = data.message ?? `HTTP ${response.status}`;\n logger.warn(\n {\n boundary: \"plugin-phone\",\n integration: \"twilio\",\n operation,\n statusCode: response.status,\n },\n `[phone] Twilio request failed: ${errorMsg}`,\n );\n span.failure({\n statusCode: response.status,\n errorKind: \"http_error\",\n });\n lastResult = {\n ok: false,\n status: response.status,\n error: errorMsg,\n retryCount: attempt,\n };\n if (!isTransientFailure(lastResult)) {\n return lastResult;\n }\n continue;\n }\n span.success({ statusCode: response.status });\n return {\n ok: true,\n status: response.status,\n sid: data.sid,\n retryCount: attempt,\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n logger.error(\n {\n boundary: \"plugin-phone\",\n integration: \"twilio\",\n operation,\n err: error instanceof Error ? error : undefined,\n },\n `[phone] Twilio request failed: ${errorMsg}`,\n );\n span.failure({\n error,\n errorKind: \"network_error\",\n });\n lastResult = {\n ok: false,\n status: null,\n error: errorMsg,\n retryCount: attempt,\n };\n }\n }\n\n return lastResult as TwilioDeliveryResult;\n}\n\nexport async function sendTwilioSms(args: {\n credentials: TwilioCredentials;\n to: string;\n body: string;\n}): Promise<TwilioDeliveryResult> {\n const { credentials, to, body } = args;\n const validationError = validateTwilioRequestInputs({\n credentials,\n to,\n messageField: \"body\",\n message: body,\n });\n if (validationError) return validationFailure(validationError);\n\n const result = await sendTwilioRequest({\n credentials,\n path: \"/Messages.json\",\n payload: new URLSearchParams({\n To: to,\n From: credentials.fromPhoneNumber,\n Body: body,\n }),\n });\n\n if (!result.ok) {\n return result;\n }\n\n return {\n ...result,\n billing: calculateTwilioSmsBilling(body, resolveSmsCostPerSegment()),\n };\n}\n\nfunction escapeXml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\nexport async function sendTwilioVoiceCall(args: {\n credentials: TwilioCredentials;\n to: string;\n message: string;\n}): Promise<TwilioDeliveryResult> {\n const { credentials, to, message } = args;\n const validationError = validateTwilioRequestInputs({\n credentials,\n to,\n messageField: \"message\",\n message,\n });\n if (validationError) return validationFailure(validationError);\n\n return sendTwilioRequest({\n credentials,\n path: \"/Calls.json\",\n payload: new URLSearchParams({\n To: to,\n From: credentials.fromPhoneNumber,\n Twiml: `<Response><Say>${escapeXml(message)}</Say></Response>`,\n }),\n });\n}\n"],"mappings":"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","names":[]}
package/dist/ui.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export { Chat as PhoneCompanionChat } from "./companion/components/Chat.tsx";
2
+ export { Pairing as PhoneCompanionPairing } from "./companion/components/Pairing.tsx";
3
+ export { PhoneCompanionApp } from "./companion/components/PhoneCompanionApp.tsx";
4
+ export { RemoteSession as PhoneCompanionRemoteSession } from "./companion/components/RemoteSession.tsx";
5
+ export { PhoneAppView } from "./components/PhoneAppView.tsx";
6
+ export { PhoneView } from "./components/PhoneView.tsx";
7
+ export { PHONE_APP_NAME, phoneApp, registerPhoneApp, } from "./components/phone-app.ts";
8
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AACjF,OAAO,EAAE,aAAa,IAAI,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACxG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EACL,cAAc,EACd,QAAQ,EACR,gBAAgB,GACjB,MAAM,2BAA2B,CAAC"}
package/dist/ui.js ADDED
@@ -0,0 +1,23 @@
1
+ import { Chat } from "./companion/components/Chat.js";
2
+ import { Pairing } from "./companion/components/Pairing.js";
3
+ import { PhoneCompanionApp } from "./companion/components/PhoneCompanionApp.js";
4
+ import { RemoteSession } from "./companion/components/RemoteSession.js";
5
+ import { PhoneAppView } from "./components/PhoneAppView.js";
6
+ import { PhoneView } from "./components/PhoneView.js";
7
+ import {
8
+ PHONE_APP_NAME,
9
+ phoneApp,
10
+ registerPhoneApp
11
+ } from "./components/phone-app.js";
12
+ export {
13
+ PHONE_APP_NAME,
14
+ PhoneAppView,
15
+ PhoneCompanionApp,
16
+ Chat as PhoneCompanionChat,
17
+ Pairing as PhoneCompanionPairing,
18
+ RemoteSession as PhoneCompanionRemoteSession,
19
+ PhoneView,
20
+ phoneApp,
21
+ registerPhoneApp
22
+ };
23
+ //# sourceMappingURL=ui.js.map
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui.ts"],"sourcesContent":["export { Chat as PhoneCompanionChat } from \"./companion/components/Chat.js\";\nexport { Pairing as PhoneCompanionPairing } from \"./companion/components/Pairing.js\";\nexport { PhoneCompanionApp } from \"./companion/components/PhoneCompanionApp.js\";\nexport { RemoteSession as PhoneCompanionRemoteSession } from \"./companion/components/RemoteSession.js\";\nexport { PhoneAppView } from \"./components/PhoneAppView.js\";\nexport { PhoneView } from \"./components/PhoneView.js\";\nexport {\n PHONE_APP_NAME,\n phoneApp,\n registerPhoneApp,\n} from \"./components/phone-app.js\";\n"],"mappings":"AAAA,SAAiB,YAA0B;AAC3C,SAAoB,eAA6B;AACjD,SAAS,yBAAyB;AAClC,SAA0B,qBAAmC;AAC7D,SAAS,oBAAoB;AAC7B,SAAS,iBAAiB;AAC1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}