@ravi-hq/ravi 0.4.1 → 0.5.1

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.
@@ -1,5 +1,5 @@
1
1
  import type { RaviClient } from "../client.js";
2
- import type { EmailEvent } from "../types.js";
2
+ import type { EmailEvent, BaseGatewayContext } from "../types.js";
3
3
  /** Configuration for a single email account within the ravi-email channel. */
4
4
  export interface EmailAccountConfig {
5
5
  /** The identity UUID this account is bound to. */
@@ -41,6 +41,8 @@ export interface EmailInboundEnvelope {
41
41
  lastMessageId: number;
42
42
  };
43
43
  }
44
+ /** Context passed by the ChannelManager to `gateway.startAccount()`. */
45
+ export type EmailGatewayContext = BaseGatewayContext<EmailAccountConfig>;
44
46
  /**
45
47
  * Creates the ravi-email channel definition for OpenClaw.
46
48
  *
@@ -69,17 +71,22 @@ export declare function createEmailChannel(): {
69
71
  /**
70
72
  * List all configured account IDs (identity UUIDs) for the ravi-email channel.
71
73
  *
72
- * @param cfg - The full OpenClaw plugin configuration object.
74
+ * Reads from the plugin config path first (set by `openclaw ravi login`),
75
+ * then falls back to the standard `channels.ravi-email.accounts` path.
76
+ * Deduplicates when both sources contain the same UUID.
77
+ *
78
+ * @param cfg - The full OpenClaw gateway configuration object.
73
79
  * @returns Array of identity UUID strings.
74
80
  */
75
81
  listAccountIds: (cfg: Record<string, unknown>) => string[];
76
82
  /**
77
83
  * Resolve an account configuration by its ID (identity UUID).
78
84
  *
79
- * Returns a default empty config if the account is not found rather than
80
- * throwing, so callers can handle missing accounts gracefully.
85
+ * Returns a minimal config with just the UUID if the account is not
86
+ * found in the standard channels path. The gateway adapter will fetch
87
+ * identity details at startup.
81
88
  *
82
- * @param cfg - The full OpenClaw plugin configuration object.
89
+ * @param cfg - The full OpenClaw gateway configuration object.
83
90
  * @param accountId - The identity UUID to look up.
84
91
  * @returns The resolved {@link EmailAccountConfig}.
85
92
  */
@@ -94,7 +101,7 @@ export declare function createEmailChannel(): {
94
101
  *
95
102
  * @param event - The raw {@link EmailEvent} from the SSE stream.
96
103
  * @param account - The {@link EmailAccountConfig} for the receiving identity.
97
- * @returns A normalized {@link EmailInboundEnvelope} (never null).
104
+ * @returns A normalized {@link EmailInboundEnvelope}. Current implementation always returns non-null.
98
105
  */
99
106
  normalizeInbound(event: EmailEvent, account: EmailAccountConfig): EmailInboundEnvelope | null;
100
107
  outbound: {
@@ -134,6 +141,27 @@ export declare function createEmailChannel(): {
134
141
  * @returns The deterministic session key string.
135
142
  */
136
143
  getSessionKey(agentId: string, accountId: string, threadId: string): string;
144
+ gateway: {
145
+ /**
146
+ * Start monitoring inbound emails for a single identity account.
147
+ *
148
+ * Called by the ChannelManager once per account. Opens an SSE stream
149
+ * to the Ravi backend, dispatches inbound emails through the OpenClaw
150
+ * reply pipeline, and blocks until the abort signal fires.
151
+ *
152
+ * This gateway adapter is the ChannelManager-driven path, used when
153
+ * OpenClaw manages channel lifecycles directly. The `normalizeInbound` +
154
+ * `RaviListenerService` path in service.ts is the legacy self-managed
155
+ * approach for environments where the gateway doesn't call startAccount.
156
+ *
157
+ * Owner/self senders get full dispatch with reply delivery.
158
+ * External senders trigger an agent turn but reply delivery is suppressed.
159
+ *
160
+ * @param ctx - Gateway context provided by the ChannelManager.
161
+ * @returns Promise that resolves when the account is stopped.
162
+ */
163
+ startAccount: (ctx: EmailGatewayContext) => Promise<void>;
164
+ };
137
165
  };
138
166
  /** The singleton ravi-email channel instance. */
139
167
  export declare const raviEmailChannel: {
@@ -152,17 +180,22 @@ export declare const raviEmailChannel: {
152
180
  /**
153
181
  * List all configured account IDs (identity UUIDs) for the ravi-email channel.
154
182
  *
155
- * @param cfg - The full OpenClaw plugin configuration object.
183
+ * Reads from the plugin config path first (set by `openclaw ravi login`),
184
+ * then falls back to the standard `channels.ravi-email.accounts` path.
185
+ * Deduplicates when both sources contain the same UUID.
186
+ *
187
+ * @param cfg - The full OpenClaw gateway configuration object.
156
188
  * @returns Array of identity UUID strings.
157
189
  */
158
190
  listAccountIds: (cfg: Record<string, unknown>) => string[];
159
191
  /**
160
192
  * Resolve an account configuration by its ID (identity UUID).
161
193
  *
162
- * Returns a default empty config if the account is not found rather than
163
- * throwing, so callers can handle missing accounts gracefully.
194
+ * Returns a minimal config with just the UUID if the account is not
195
+ * found in the standard channels path. The gateway adapter will fetch
196
+ * identity details at startup.
164
197
  *
165
- * @param cfg - The full OpenClaw plugin configuration object.
198
+ * @param cfg - The full OpenClaw gateway configuration object.
166
199
  * @param accountId - The identity UUID to look up.
167
200
  * @returns The resolved {@link EmailAccountConfig}.
168
201
  */
@@ -177,7 +210,7 @@ export declare const raviEmailChannel: {
177
210
  *
178
211
  * @param event - The raw {@link EmailEvent} from the SSE stream.
179
212
  * @param account - The {@link EmailAccountConfig} for the receiving identity.
180
- * @returns A normalized {@link EmailInboundEnvelope} (never null).
213
+ * @returns A normalized {@link EmailInboundEnvelope}. Current implementation always returns non-null.
181
214
  */
182
215
  normalizeInbound(event: EmailEvent, account: EmailAccountConfig): EmailInboundEnvelope | null;
183
216
  outbound: {
@@ -217,5 +250,26 @@ export declare const raviEmailChannel: {
217
250
  * @returns The deterministic session key string.
218
251
  */
219
252
  getSessionKey(agentId: string, accountId: string, threadId: string): string;
253
+ gateway: {
254
+ /**
255
+ * Start monitoring inbound emails for a single identity account.
256
+ *
257
+ * Called by the ChannelManager once per account. Opens an SSE stream
258
+ * to the Ravi backend, dispatches inbound emails through the OpenClaw
259
+ * reply pipeline, and blocks until the abort signal fires.
260
+ *
261
+ * This gateway adapter is the ChannelManager-driven path, used when
262
+ * OpenClaw manages channel lifecycles directly. The `normalizeInbound` +
263
+ * `RaviListenerService` path in service.ts is the legacy self-managed
264
+ * approach for environments where the gateway doesn't call startAccount.
265
+ *
266
+ * Owner/self senders get full dispatch with reply delivery.
267
+ * External senders trigger an agent turn but reply delivery is suppressed.
268
+ *
269
+ * @param ctx - Gateway context provided by the ChannelManager.
270
+ * @returns Promise that resolves when the account is stopped.
271
+ */
272
+ startAccount: (ctx: EmailGatewayContext) => Promise<void>;
273
+ };
220
274
  };
221
275
  //# sourceMappingURL=email.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;CAEf;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB;;;;;;;;;;;;;QAkB5B;;;;;WAKG;8BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,EAAE;QAOxD;;;;;;;;;WASG;8BAEI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aACjB,MAAM,KAChB,kBAAkB;;IAgBvB;;;;;;;;;;OAUG;4BAEM,UAAU,WACR,kBAAkB,GAC1B,oBAAoB,GAAG,IAAI;;;QAuB5B;;;;;;;;;;WAUG;2BACsB;YACvB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,UAAU,CAAC;YACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;SACpC,KAAG,OAAO,CAAC;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAoB9C;;;;;;;;;;;;;OAaG;2BACoB,MAAM,aAAa,MAAM,YAAY,MAAM,GAAG,MAAM;EAI9E;AAED,iDAAiD;AACjD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;QAvIvB;;;;;WAKG;8BACmB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,EAAE;QAOxD;;;;;;;;;WASG;8BAEI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aACjB,MAAM,KAChB,kBAAkB;;IAgBvB;;;;;;;;;;OAUG;4BAEM,UAAU,WACR,kBAAkB,GAC1B,oBAAoB,GAAG,IAAI;;;QAuB5B;;;;;;;;;;WAUG;2BACsB;YACvB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,UAAU,CAAC;YACnB,aAAa,CAAC,EAAE,kBAAkB,CAAC;SACpC,KAAG,OAAO,CAAC;YAAE,EAAE,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAoB9C;;;;;;;;;;;;;OAaG;2BACoB,MAAM,aAAa,MAAM,YAAY,MAAM,GAAG,MAAM;CAO3B,CAAC"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"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"}
@@ -29,30 +29,44 @@ export function createEmailChannel() {
29
29
  /**
30
30
  * List all configured account IDs (identity UUIDs) for the ravi-email channel.
31
31
  *
32
- * @param cfg - The full OpenClaw plugin configuration object.
32
+ * Reads from the plugin config path first (set by `openclaw ravi login`),
33
+ * then falls back to the standard `channels.ravi-email.accounts` path.
34
+ * Deduplicates when both sources contain the same UUID.
35
+ *
36
+ * @param cfg - The full OpenClaw gateway configuration object.
33
37
  * @returns Array of identity UUID strings.
34
38
  */
35
39
  listAccountIds: (cfg) => {
40
+ // Primary: read from plugin config (set by `openclaw ravi login`)
41
+ const plugins = cfg.plugins;
42
+ const uuid = plugins?.entries?.ravi?.config?.identityUuid;
43
+ // Fallback: standard channel accounts path
36
44
  const channels = cfg.channels;
37
- return Object.keys(channels?.["ravi-email"]?.accounts ?? {});
45
+ const channelIds = Object.keys(channels?.["ravi-email"]?.accounts ?? {});
46
+ // Deduplicate — plugin config uuid takes priority
47
+ if (uuid) {
48
+ return [uuid, ...channelIds.filter((id) => id !== uuid)];
49
+ }
50
+ return channelIds;
38
51
  },
39
52
  /**
40
53
  * Resolve an account configuration by its ID (identity UUID).
41
54
  *
42
- * Returns a default empty config if the account is not found rather than
43
- * throwing, so callers can handle missing accounts gracefully.
55
+ * Returns a minimal config with just the UUID if the account is not
56
+ * found in the standard channels path. The gateway adapter will fetch
57
+ * identity details at startup.
44
58
  *
45
- * @param cfg - The full OpenClaw plugin configuration object.
59
+ * @param cfg - The full OpenClaw gateway configuration object.
46
60
  * @param accountId - The identity UUID to look up.
47
61
  * @returns The resolved {@link EmailAccountConfig}.
48
62
  */
49
63
  resolveAccount: (cfg, accountId) => {
50
64
  const channels = cfg.channels;
51
65
  const found = channels?.["ravi-email"]?.accounts?.[accountId];
52
- if (!found) {
53
- console.warn(`[ravi] Email account ${accountId} not found in config — using empty defaults`);
54
- }
55
- return found ?? {
66
+ if (found)
67
+ return found;
68
+ // Fallback: return minimal config — gateway adapter fetches details at startup
69
+ return {
56
70
  identityUuid: accountId,
57
71
  identityName: "",
58
72
  email: "",
@@ -68,7 +82,7 @@ export function createEmailChannel() {
68
82
  *
69
83
  * @param event - The raw {@link EmailEvent} from the SSE stream.
70
84
  * @param account - The {@link EmailAccountConfig} for the receiving identity.
71
- * @returns A normalized {@link EmailInboundEnvelope} (never null).
85
+ * @returns A normalized {@link EmailInboundEnvelope}. Current implementation always returns non-null.
72
86
  */
73
87
  normalizeInbound(event, account) {
74
88
  const isReadOnly = event.sender_type !== "owner" && event.sender_type !== "self";
@@ -136,6 +150,250 @@ export function createEmailChannel() {
136
150
  getSessionKey(agentId, accountId, threadId) {
137
151
  return `agent:${agentId}:ravi-email:${accountId}:thread:${threadId}`;
138
152
  },
153
+ // ─── Gateway Adapter ──────────────────────────────────────────────────
154
+ gateway: {
155
+ /**
156
+ * Start monitoring inbound emails for a single identity account.
157
+ *
158
+ * Called by the ChannelManager once per account. Opens an SSE stream
159
+ * to the Ravi backend, dispatches inbound emails through the OpenClaw
160
+ * reply pipeline, and blocks until the abort signal fires.
161
+ *
162
+ * This gateway adapter is the ChannelManager-driven path, used when
163
+ * OpenClaw manages channel lifecycles directly. The `normalizeInbound` +
164
+ * `RaviListenerService` path in service.ts is the legacy self-managed
165
+ * approach for environments where the gateway doesn't call startAccount.
166
+ *
167
+ * Owner/self senders get full dispatch with reply delivery.
168
+ * External senders trigger an agent turn but reply delivery is suppressed.
169
+ *
170
+ * @param ctx - Gateway context provided by the ChannelManager.
171
+ * @returns Promise that resolves when the account is stopped.
172
+ */
173
+ startAccount: async (ctx) => {
174
+ const log = ctx.log ?? {
175
+ info: (...args) => console.log("[ravi-email]", ...args),
176
+ warn: (...args) => console.warn("[ravi-email]", ...args),
177
+ error: (...args) => console.error("[ravi-email]", ...args),
178
+ };
179
+ // Lazy imports — only loaded when the gateway actually starts an account.
180
+ // This avoids pulling in runtime/auth/config/client/sse in test environments
181
+ // where gateway.startAccount() is never called.
182
+ let getRaviRuntime, loadAuth, updateAuth, RAVI_API_URL, RaviClient, RaviSSEClient;
183
+ try {
184
+ ({ getRaviRuntime } = await import("../runtime.js"));
185
+ ({ loadAuth, updateAuth } = await import("../auth.js"));
186
+ ({ RAVI_API_URL } = await import("../config.js"));
187
+ ({ RaviClient } = await import("../client.js"));
188
+ ({ RaviSSEClient } = await import("../sse.js"));
189
+ }
190
+ catch (err) {
191
+ const detail = err instanceof Error ? err.message : String(err);
192
+ log.error(`[ravi-email] Failed to load dependencies: ${detail}. Is the plugin built correctly?`);
193
+ return;
194
+ }
195
+ const runtime = getRaviRuntime();
196
+ const auth = loadAuth();
197
+ if (!auth?.access_token) {
198
+ log.error("[ravi-email] No auth credentials — cannot start email channel. Run 'openclaw ravi login'.");
199
+ return;
200
+ }
201
+ // Track the current token — onTokenRefresh updates this when the
202
+ // REST client auto-refreshes on 401, so the SSE client (created after
203
+ // the first REST call) gets the fresh token instead of the stale one.
204
+ let currentToken = auth.access_token;
205
+ let sse = null;
206
+ // Build HTTP client for identity lookup + outbound replies
207
+ const client = new RaviClient({
208
+ apiUrl: RAVI_API_URL,
209
+ token: auth.access_token,
210
+ refreshToken: auth.refresh_token,
211
+ identityUuid: ctx.accountId,
212
+ onTokenRefresh: (newToken) => {
213
+ currentToken = newToken;
214
+ try {
215
+ updateAuth({ access_token: newToken });
216
+ }
217
+ catch (err) {
218
+ const detail = err instanceof Error ? err.message : String(err);
219
+ log.error(`[ravi-email] Failed to persist refreshed token: ${detail}`);
220
+ }
221
+ sse?.updateToken(newToken);
222
+ },
223
+ });
224
+ // Fetch identity details (email address, name) if not in config
225
+ let email = ctx.account.email;
226
+ let identityName = ctx.account.identityName;
227
+ if (!email) {
228
+ try {
229
+ const identities = await client.listIdentities();
230
+ const identity = identities.find((i) => i.uuid === ctx.accountId);
231
+ email = identity?.inbox ?? "";
232
+ identityName = identity?.display_name ?? identity?.name ?? ctx.accountId;
233
+ }
234
+ catch (err) {
235
+ if (err?.name === "RaviApiError") {
236
+ const status = err.status;
237
+ log.error(`[ravi-email] Failed to fetch identity details (HTTP ${status}): ${err.message}` +
238
+ (status === 401 ? ". Token may be expired — run 'openclaw ravi login'." : ""));
239
+ }
240
+ else {
241
+ const detail = err instanceof Error ? err.message : String(err);
242
+ log.error(`[ravi-email] Failed to fetch identity details: ${detail}`);
243
+ }
244
+ return;
245
+ }
246
+ }
247
+ if (!email) {
248
+ log.error(`[ravi-email] Identity ${ctx.accountId} has no email address — cannot start email channel`);
249
+ return;
250
+ }
251
+ log.info(`[ravi-email] Starting email channel for ${identityName} (${email})`);
252
+ // Build SSE client using currentToken — which may have been refreshed
253
+ // by the listIdentities() call above if the original token was expired.
254
+ // onAuthFailure triggers a REST call to force auto-refresh, then returns
255
+ // the updated currentToken (already set by onTokenRefresh callback).
256
+ sse = new RaviSSEClient({
257
+ apiUrl: RAVI_API_URL,
258
+ token: currentToken,
259
+ identityUuid: ctx.accountId,
260
+ onAuthFailure: async () => {
261
+ const tokenBefore = currentToken;
262
+ try {
263
+ await client.listIdentities();
264
+ }
265
+ catch (err) {
266
+ const detail = err instanceof Error ? err.message : String(err);
267
+ log.error(`[ravi-email] Auth refresh failed during SSE reconnect: ${detail}. Run 'openclaw ravi login' to re-authenticate.`);
268
+ return null;
269
+ }
270
+ if (currentToken === tokenBefore) {
271
+ log.warn(`[ravi-email] Auth refresh did not produce a new token — SSE auth failure is not recoverable via REST refresh`);
272
+ return null;
273
+ }
274
+ return currentToken;
275
+ },
276
+ });
277
+ sse.onEmail((event) => {
278
+ try {
279
+ // Classify sender: owner/self get full reply; external/unknown wakes agent but suppresses delivery
280
+ const isAuthorizedSender = event.sender_type === "owner" || event.sender_type === "self";
281
+ const lastMessageId = Number(event.id) || 0;
282
+ if (!lastMessageId) {
283
+ log.warn(`[ravi-email] Event has non-numeric id "${event.id}" — reply threading may fail`);
284
+ }
285
+ const subject = event.subject;
286
+ // Resolve agent route — maps (channel, accountId) → agentId + session key
287
+ const route = runtime.channel.routing.resolveAgentRoute({
288
+ cfg: ctx.cfg,
289
+ channel: "ravi-email",
290
+ accountId: ctx.accountId,
291
+ peer: { kind: "direct", id: event.thread_id },
292
+ });
293
+ const sessionKey = route.sessionKey;
294
+ // Build MsgContext for dispatch
295
+ const msgCtx = {
296
+ Body: event.text_content || event.subject,
297
+ BodyForAgent: event.text_content || event.subject,
298
+ From: `ravi-email:${event.from_email}`,
299
+ To: `ravi-email:${email}`,
300
+ SessionKey: sessionKey,
301
+ AccountId: ctx.accountId,
302
+ ChatType: "direct",
303
+ Provider: "ravi-email",
304
+ Surface: "ravi-email",
305
+ OriginatingChannel: "ravi-email",
306
+ OriginatingTo: ctx.accountId,
307
+ SenderName: event.from_display_name || event.from_email,
308
+ SenderId: event.from_email,
309
+ Timestamp: Date.now(),
310
+ CommandAuthorized: isAuthorizedSender,
311
+ MessageSid: `ravi-email:${event.id}`,
312
+ };
313
+ // Record inbound session so follow-ups route back through this email thread
314
+ try {
315
+ runtime.channel.session.recordInboundSession({
316
+ storePath: runtime.channel.session.resolveStorePath(ctx.cfg.session?.store, { agentId: route.agentId }),
317
+ sessionKey,
318
+ ctx: msgCtx,
319
+ updateLastRoute: {
320
+ sessionKey,
321
+ channel: "ravi-email",
322
+ to: ctx.accountId,
323
+ accountId: ctx.accountId,
324
+ threadId: event.thread_id,
325
+ },
326
+ onRecordError: (err) => {
327
+ const detail = err instanceof Error ? err.message : String(err);
328
+ log.error(`[ravi-email] Session record error for thread ${event.thread_id}: ${detail}`);
329
+ },
330
+ });
331
+ }
332
+ catch (err) {
333
+ const detail = err instanceof Error ? err.message : String(err);
334
+ log.error(`[ravi-email] recordInboundSession threw for thread ${event.thread_id}: ${detail}`);
335
+ }
336
+ // Dispatch — triggers AI agent turn.
337
+ // For external senders: AI wakes up, can use tools, but deliver is suppressed.
338
+ runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
339
+ ctx: msgCtx,
340
+ cfg: ctx.cfg,
341
+ dispatcherOptions: {
342
+ deliver: async (payload) => {
343
+ if (!payload.text) {
344
+ log.info(`[ravi-email] Deliver called with empty text for thread ${event.thread_id} — skipping`);
345
+ return;
346
+ }
347
+ // Suppress reply delivery for external senders
348
+ if (!isAuthorizedSender) {
349
+ log.info(`[ravi-email] Suppressing reply to external sender ${event.from_email}`);
350
+ return;
351
+ }
352
+ try {
353
+ await client.replyToEmail(lastMessageId, wrapInHtml(payload.text), subject);
354
+ }
355
+ catch (err) {
356
+ const detail = err instanceof Error ? err.message : String(err);
357
+ const status = err?.status;
358
+ log.error(`[ravi-email] Failed to deliver reply to thread ${event.thread_id}` +
359
+ `${status ? ` (HTTP ${status})` : ""}: ${detail}`);
360
+ }
361
+ },
362
+ onError: (err) => {
363
+ log.error(`[ravi-email] Dispatch error: ${err}`);
364
+ },
365
+ },
366
+ }).catch((err) => {
367
+ const detail = err instanceof Error ? err.message : String(err);
368
+ log.error(`[ravi-email] dispatchReply failed for thread ${event.thread_id}: ${detail}`);
369
+ });
370
+ }
371
+ catch (err) {
372
+ const detail = err instanceof Error ? err.message : String(err);
373
+ log.error(`[ravi-email] Unhandled error processing event ${event.id}: ${detail}`);
374
+ }
375
+ });
376
+ sse.onReconnect(() => {
377
+ log.info(`[ravi-email] SSE reconnecting for ${identityName}`);
378
+ });
379
+ sse.connect();
380
+ log.info(`[ravi-email] SSE connected for ${identityName} (${ctx.accountId})`);
381
+ // Block until gateway signals shutdown
382
+ await new Promise((resolve) => {
383
+ if (ctx.abortSignal.aborted) {
384
+ log.info(`[ravi-email] Stopping email channel for ${identityName} (already aborted)`);
385
+ sse.disconnect();
386
+ resolve();
387
+ return;
388
+ }
389
+ ctx.abortSignal.addEventListener("abort", () => {
390
+ log.info(`[ravi-email] Stopping email channel for ${identityName}`);
391
+ sse.disconnect();
392
+ resolve();
393
+ });
394
+ });
395
+ },
396
+ },
139
397
  };
140
398
  }
141
399
  /** The singleton ravi-email channel instance. */
@@ -1 +1 @@
1
- {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/channels/email.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { RaviClient } from "../client.js";
2
- import type { SmsEvent } from "../types.js";
2
+ import type { SmsEvent, BaseGatewayContext } from "../types.js";
3
3
  /** Configuration for a single SMS account within the ravi-sms channel. */
4
4
  export interface SmsAccountConfig {
5
5
  /** The identity UUID this account is bound to. */
@@ -35,6 +35,8 @@ export interface SmsInboundEnvelope {
35
35
  conversationId: string;
36
36
  };
37
37
  }
38
+ /** Context passed by the ChannelManager to `gateway.startAccount()`. */
39
+ export type SmsGatewayContext = BaseGatewayContext<SmsAccountConfig>;
38
40
  /**
39
41
  * Creates the ravi-sms channel definition for OpenClaw.
40
42
  *
@@ -63,17 +65,22 @@ export declare function createSmsChannel(): {
63
65
  /**
64
66
  * List all configured account IDs (identity UUIDs) for the ravi-sms channel.
65
67
  *
66
- * @param cfg - The full OpenClaw plugin configuration object.
68
+ * Reads from the plugin config path first (set by `openclaw ravi login`),
69
+ * then falls back to the standard `channels.ravi-sms.accounts` path.
70
+ * Deduplicates when both sources contain the same UUID.
71
+ *
72
+ * @param cfg - The full OpenClaw gateway configuration object.
67
73
  * @returns Array of identity UUID strings.
68
74
  */
69
75
  listAccountIds: (cfg: Record<string, unknown>) => string[];
70
76
  /**
71
77
  * Resolve an account configuration by its ID (identity UUID).
72
78
  *
73
- * Returns a default empty config if the account is not found rather than
74
- * throwing, so callers can handle missing accounts gracefully.
79
+ * Returns a minimal config with just the UUID if the account is not
80
+ * found in the standard channels path. The gateway adapter will fetch
81
+ * identity details at startup.
75
82
  *
76
- * @param cfg - The full OpenClaw plugin configuration object.
83
+ * @param cfg - The full OpenClaw gateway configuration object.
77
84
  * @param accountId - The identity UUID to look up.
78
85
  * @returns The resolved {@link SmsAccountConfig}.
79
86
  */
@@ -88,7 +95,7 @@ export declare function createSmsChannel(): {
88
95
  *
89
96
  * @param event - The raw {@link SmsEvent} from the SSE stream.
90
97
  * @param account - The {@link SmsAccountConfig} for the receiving identity.
91
- * @returns A normalized {@link SmsInboundEnvelope} (never null).
98
+ * @returns A normalized {@link SmsInboundEnvelope}. Current implementation always returns non-null.
92
99
  */
93
100
  normalizeInbound(event: SmsEvent, account: SmsAccountConfig): SmsInboundEnvelope | null;
94
101
  outbound: {
@@ -132,6 +139,30 @@ export declare function createSmsChannel(): {
132
139
  * @returns The deterministic session key string.
133
140
  */
134
141
  getSessionKey(agentId: string, accountId: string, phoneNumber: string): string;
142
+ gateway: {
143
+ /**
144
+ * Start monitoring inbound SMS for a single identity account.
145
+ *
146
+ * Called by the ChannelManager once per account. Opens an SSE stream
147
+ * to the Ravi backend, dispatches inbound SMS through the OpenClaw
148
+ * reply pipeline, and blocks until the abort signal fires.
149
+ *
150
+ * Bails out early if the identity has no phone number — SMS requires
151
+ * a provisioned phone number.
152
+ *
153
+ * Owner/self senders get full dispatch with reply delivery.
154
+ * External senders trigger an agent turn but reply delivery is suppressed.
155
+ *
156
+ * This gateway adapter is the ChannelManager-driven path, used when
157
+ * OpenClaw manages channel lifecycles directly. The `normalizeInbound` +
158
+ * `RaviListenerService` path in service.ts is the legacy self-managed
159
+ * approach for environments where the gateway doesn't call startAccount.
160
+ *
161
+ * @param ctx - Gateway context provided by the ChannelManager.
162
+ * @returns Promise that resolves when the account is stopped.
163
+ */
164
+ startAccount: (ctx: SmsGatewayContext) => Promise<void>;
165
+ };
135
166
  };
136
167
  /** The singleton ravi-sms channel instance. */
137
168
  export declare const raviSmsChannel: {
@@ -150,17 +181,22 @@ export declare const raviSmsChannel: {
150
181
  /**
151
182
  * List all configured account IDs (identity UUIDs) for the ravi-sms channel.
152
183
  *
153
- * @param cfg - The full OpenClaw plugin configuration object.
184
+ * Reads from the plugin config path first (set by `openclaw ravi login`),
185
+ * then falls back to the standard `channels.ravi-sms.accounts` path.
186
+ * Deduplicates when both sources contain the same UUID.
187
+ *
188
+ * @param cfg - The full OpenClaw gateway configuration object.
154
189
  * @returns Array of identity UUID strings.
155
190
  */
156
191
  listAccountIds: (cfg: Record<string, unknown>) => string[];
157
192
  /**
158
193
  * Resolve an account configuration by its ID (identity UUID).
159
194
  *
160
- * Returns a default empty config if the account is not found rather than
161
- * throwing, so callers can handle missing accounts gracefully.
195
+ * Returns a minimal config with just the UUID if the account is not
196
+ * found in the standard channels path. The gateway adapter will fetch
197
+ * identity details at startup.
162
198
  *
163
- * @param cfg - The full OpenClaw plugin configuration object.
199
+ * @param cfg - The full OpenClaw gateway configuration object.
164
200
  * @param accountId - The identity UUID to look up.
165
201
  * @returns The resolved {@link SmsAccountConfig}.
166
202
  */
@@ -175,7 +211,7 @@ export declare const raviSmsChannel: {
175
211
  *
176
212
  * @param event - The raw {@link SmsEvent} from the SSE stream.
177
213
  * @param account - The {@link SmsAccountConfig} for the receiving identity.
178
- * @returns A normalized {@link SmsInboundEnvelope} (never null).
214
+ * @returns A normalized {@link SmsInboundEnvelope}. Current implementation always returns non-null.
179
215
  */
180
216
  normalizeInbound(event: SmsEvent, account: SmsAccountConfig): SmsInboundEnvelope | null;
181
217
  outbound: {
@@ -219,5 +255,29 @@ export declare const raviSmsChannel: {
219
255
  * @returns The deterministic session key string.
220
256
  */
221
257
  getSessionKey(agentId: string, accountId: string, phoneNumber: string): string;
258
+ gateway: {
259
+ /**
260
+ * Start monitoring inbound SMS for a single identity account.
261
+ *
262
+ * Called by the ChannelManager once per account. Opens an SSE stream
263
+ * to the Ravi backend, dispatches inbound SMS through the OpenClaw
264
+ * reply pipeline, and blocks until the abort signal fires.
265
+ *
266
+ * Bails out early if the identity has no phone number — SMS requires
267
+ * a provisioned phone number.
268
+ *
269
+ * Owner/self senders get full dispatch with reply delivery.
270
+ * External senders trigger an agent turn but reply delivery is suppressed.
271
+ *
272
+ * This gateway adapter is the ChannelManager-driven path, used when
273
+ * OpenClaw manages channel lifecycles directly. The `normalizeInbound` +
274
+ * `RaviListenerService` path in service.ts is the legacy self-managed
275
+ * approach for environments where the gateway doesn't call startAccount.
276
+ *
277
+ * @param ctx - Gateway context provided by the ChannelManager.
278
+ * @returns Promise that resolves when the account is stopped.
279
+ */
280
+ startAccount: (ctx: SmsGatewayContext) => Promise<void>;
281
+ };
222
282
  };
223
283
  //# sourceMappingURL=sms.d.ts.map