@elizaos/plugin-inbox 2.0.3-beta.6 → 2.0.3-beta.7

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 (119) hide show
  1. package/dist/actions/inbox.d.ts +69 -0
  2. package/dist/actions/inbox.d.ts.map +1 -0
  3. package/dist/actions/inbox.js +345 -0
  4. package/dist/actions/inbox.js.map +1 -0
  5. package/dist/components/inbox/InboxSpatialView.d.ts +54 -0
  6. package/dist/components/inbox/InboxSpatialView.d.ts.map +1 -0
  7. package/dist/components/inbox/InboxSpatialView.js +171 -0
  8. package/dist/components/inbox/InboxSpatialView.js.map +1 -0
  9. package/dist/components/inbox/InboxView.d.ts +64 -0
  10. package/dist/components/inbox/InboxView.d.ts.map +1 -0
  11. package/dist/components/inbox/InboxView.js +169 -0
  12. package/dist/components/inbox/InboxView.js.map +1 -0
  13. package/dist/components/inbox/inbox-view-bundle.d.ts +2 -0
  14. package/dist/components/inbox/inbox-view-bundle.d.ts.map +1 -0
  15. package/dist/components/inbox/inbox-view-bundle.js +5 -0
  16. package/dist/components/inbox/inbox-view-bundle.js.map +1 -0
  17. package/dist/db/index.d.ts +3 -0
  18. package/dist/db/index.d.ts.map +1 -0
  19. package/dist/db/index.js +3 -0
  20. package/dist/db/index.js.map +1 -0
  21. package/dist/db/schema.d.ts +1729 -0
  22. package/dist/db/schema.d.ts.map +1 -0
  23. package/dist/db/schema.js +79 -0
  24. package/dist/db/schema.js.map +1 -0
  25. package/dist/db/sql.d.ts +32 -0
  26. package/dist/db/sql.d.ts.map +1 -0
  27. package/dist/db/sql.js +130 -0
  28. package/dist/db/sql.js.map +1 -0
  29. package/dist/inbox/channel-deep-links.d.ts +7 -0
  30. package/dist/inbox/channel-deep-links.d.ts.map +1 -0
  31. package/dist/inbox/channel-deep-links.js +97 -0
  32. package/dist/inbox/channel-deep-links.js.map +1 -0
  33. package/dist/inbox/config.d.ts +7 -0
  34. package/dist/inbox/config.d.ts.map +1 -0
  35. package/dist/inbox/config.js +61 -0
  36. package/dist/inbox/config.js.map +1 -0
  37. package/dist/inbox/email-curation.d.ts +174 -0
  38. package/dist/inbox/email-curation.d.ts.map +1 -0
  39. package/dist/inbox/email-curation.js +1056 -0
  40. package/dist/inbox/email-curation.js.map +1 -0
  41. package/dist/inbox/email-unsubscribe-types.d.ts +71 -0
  42. package/dist/inbox/email-unsubscribe-types.d.ts.map +1 -0
  43. package/dist/inbox/email-unsubscribe-types.js +1 -0
  44. package/dist/inbox/email-unsubscribe-types.js.map +1 -0
  45. package/dist/inbox/gmail-normalize.d.ts +99 -0
  46. package/dist/inbox/gmail-normalize.d.ts.map +1 -0
  47. package/dist/inbox/gmail-normalize.js +937 -0
  48. package/dist/inbox/gmail-normalize.js.map +1 -0
  49. package/dist/inbox/google-gmail-seam.d.ts +52 -0
  50. package/dist/inbox/google-gmail-seam.d.ts.map +1 -0
  51. package/dist/inbox/google-gmail-seam.js +263 -0
  52. package/dist/inbox/google-gmail-seam.js.map +1 -0
  53. package/dist/inbox/message-fetcher.d.ts +47 -0
  54. package/dist/inbox/message-fetcher.d.ts.map +1 -0
  55. package/dist/inbox/message-fetcher.js +461 -0
  56. package/dist/inbox/message-fetcher.js.map +1 -0
  57. package/dist/inbox/migration.d.ts +46 -0
  58. package/dist/inbox/migration.d.ts.map +1 -0
  59. package/dist/inbox/migration.js +114 -0
  60. package/dist/inbox/migration.js.map +1 -0
  61. package/dist/inbox/reflection.d.ts +40 -0
  62. package/dist/inbox/reflection.d.ts.map +1 -0
  63. package/dist/inbox/reflection.js +142 -0
  64. package/dist/inbox/reflection.js.map +1 -0
  65. package/dist/inbox/repository.d.ts +58 -0
  66. package/dist/inbox/repository.d.ts.map +1 -0
  67. package/dist/inbox/repository.js +376 -0
  68. package/dist/inbox/repository.js.map +1 -0
  69. package/dist/inbox/service.d.ts +149 -0
  70. package/dist/inbox/service.d.ts.map +1 -0
  71. package/dist/inbox/service.js +247 -0
  72. package/dist/inbox/service.js.map +1 -0
  73. package/dist/inbox/triage-classifier.d.ts +28 -0
  74. package/dist/inbox/triage-classifier.d.ts.map +1 -0
  75. package/dist/inbox/triage-classifier.js +306 -0
  76. package/dist/inbox/triage-classifier.js.map +1 -0
  77. package/dist/inbox/types.d.ts +124 -0
  78. package/dist/inbox/types.d.ts.map +1 -0
  79. package/dist/inbox/types.js +1 -0
  80. package/dist/inbox/types.js.map +1 -0
  81. package/dist/inbox/unsubscribe-repository.d.ts +14 -0
  82. package/dist/inbox/unsubscribe-repository.d.ts.map +1 -0
  83. package/dist/inbox/unsubscribe-repository.js +112 -0
  84. package/dist/inbox/unsubscribe-repository.js.map +1 -0
  85. package/dist/inbox/unsubscribe-service.d.ts +41 -0
  86. package/dist/inbox/unsubscribe-service.d.ts.map +1 -0
  87. package/dist/inbox/unsubscribe-service.js +351 -0
  88. package/dist/inbox/unsubscribe-service.js.map +1 -0
  89. package/dist/index.d.ts +20 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +70 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/plugin.d.ts +4 -0
  94. package/dist/plugin.d.ts.map +1 -0
  95. package/dist/plugin.js +38 -0
  96. package/dist/plugin.js.map +1 -0
  97. package/dist/providers/cross-channel-context.d.ts +21 -0
  98. package/dist/providers/cross-channel-context.d.ts.map +1 -0
  99. package/dist/providers/cross-channel-context.js +96 -0
  100. package/dist/providers/cross-channel-context.js.map +1 -0
  101. package/dist/providers/inbox-triage.d.ts +12 -0
  102. package/dist/providers/inbox-triage.d.ts.map +1 -0
  103. package/dist/providers/inbox-triage.js +98 -0
  104. package/dist/providers/inbox-triage.js.map +1 -0
  105. package/dist/register-terminal-view.d.ts +15 -0
  106. package/dist/register-terminal-view.d.ts.map +1 -0
  107. package/dist/register-terminal-view.js +21 -0
  108. package/dist/register-terminal-view.js.map +1 -0
  109. package/dist/register.d.ts +9 -0
  110. package/dist/register.d.ts.map +1 -0
  111. package/dist/register.js +5 -0
  112. package/dist/register.js.map +1 -0
  113. package/dist/types.d.ts +42 -0
  114. package/dist/types.d.ts.map +1 -0
  115. package/dist/types.js +25 -0
  116. package/dist/types.js.map +1 -0
  117. package/dist/views/bundle.js +315 -0
  118. package/dist/views/bundle.js.map +1 -0
  119. package/package.json +9 -9
@@ -0,0 +1,376 @@
1
+ import crypto from "node:crypto";
2
+ import {
3
+ executeRawSql,
4
+ parseJsonArray,
5
+ parseJsonRecord,
6
+ sqlBoolean,
7
+ sqlNumber,
8
+ sqlQuote,
9
+ sqlText,
10
+ toBoolean,
11
+ toNumber,
12
+ toText
13
+ } from "../db/sql.js";
14
+ const TRIAGE_CLASSIFICATIONS = /* @__PURE__ */ new Set([
15
+ "ignore",
16
+ "info",
17
+ "notify",
18
+ "needs_reply",
19
+ "urgent"
20
+ ]);
21
+ const TRIAGE_URGENCIES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
22
+ const OWNER_ACTIONS = /* @__PURE__ */ new Set([
23
+ "confirmed",
24
+ "reclassified",
25
+ "edited_draft",
26
+ "ignored"
27
+ ]);
28
+ function parseTriageClassification(value) {
29
+ const normalized = toText(value).trim();
30
+ if (TRIAGE_CLASSIFICATIONS.has(normalized)) {
31
+ return normalized;
32
+ }
33
+ throw new Error(
34
+ `[InboxRepository] invalid triage classification: ${normalized}`
35
+ );
36
+ }
37
+ function parseTriageUrgency(value) {
38
+ const normalized = toText(value).trim();
39
+ if (TRIAGE_URGENCIES.has(normalized)) {
40
+ return normalized;
41
+ }
42
+ throw new Error(`[InboxRepository] invalid triage urgency: ${normalized}`);
43
+ }
44
+ function parseNullableTriageClassification(value) {
45
+ const normalized = toText(value).trim();
46
+ return normalized ? parseTriageClassification(normalized) : null;
47
+ }
48
+ function parseOwnerAction(value) {
49
+ const normalized = toText(value).trim();
50
+ if (OWNER_ACTIONS.has(normalized)) {
51
+ return normalized;
52
+ }
53
+ throw new Error(`[InboxRepository] invalid owner action: ${normalized}`);
54
+ }
55
+ function parseJsonStringArray(value, label) {
56
+ const entries = parseJsonArray(value);
57
+ const strings = [];
58
+ for (const entry of entries) {
59
+ if (typeof entry !== "string") {
60
+ throw new Error(`[InboxRepository] ${label} must contain strings`);
61
+ }
62
+ strings.push(entry);
63
+ }
64
+ return strings;
65
+ }
66
+ function parseTriageEntry(row) {
67
+ return {
68
+ id: toText(row.id),
69
+ agentId: toText(row.agent_id),
70
+ source: toText(row.source),
71
+ sourceRoomId: toText(row.source_room_id) || null,
72
+ sourceEntityId: toText(row.source_entity_id) || null,
73
+ sourceMessageId: toText(row.source_message_id) || null,
74
+ channelName: toText(row.channel_name),
75
+ channelType: toText(row.channel_type),
76
+ deepLink: toText(row.deep_link) || null,
77
+ classification: parseTriageClassification(row.classification),
78
+ urgency: parseTriageUrgency(row.urgency),
79
+ confidence: toNumber(row.confidence, 0.5),
80
+ snippet: toText(row.snippet),
81
+ senderName: toText(row.sender_name) || null,
82
+ threadContext: row.thread_context ? parseJsonStringArray(row.thread_context, "thread_context") : null,
83
+ triageReasoning: toText(row.triage_reasoning) || null,
84
+ suggestedResponse: toText(row.suggested_response) || null,
85
+ draftResponse: toText(row.draft_response) || null,
86
+ autoReplied: toBoolean(row.auto_replied, false),
87
+ resolved: toBoolean(row.resolved, false),
88
+ resolvedAt: toText(row.resolved_at) || null,
89
+ createdAt: toText(row.created_at),
90
+ updatedAt: toText(row.updated_at)
91
+ };
92
+ }
93
+ function parseTriageExample(row) {
94
+ return {
95
+ id: toText(row.id),
96
+ agentId: toText(row.agent_id),
97
+ source: toText(row.source),
98
+ snippet: toText(row.snippet),
99
+ classification: parseTriageClassification(row.classification),
100
+ ownerAction: parseOwnerAction(row.owner_action),
101
+ ownerClassification: parseNullableTriageClassification(
102
+ row.owner_classification
103
+ ),
104
+ contextJson: parseJsonRecord(row.context_json),
105
+ createdAt: toText(row.created_at)
106
+ };
107
+ }
108
+ function newId() {
109
+ return crypto.randomUUID();
110
+ }
111
+ function isoNow() {
112
+ return (/* @__PURE__ */ new Date()).toISOString();
113
+ }
114
+ function sqlJsonArray(value) {
115
+ if (!value || value.length === 0) return "NULL";
116
+ return sqlQuote(JSON.stringify(value));
117
+ }
118
+ class InboxRepository {
119
+ constructor(runtime) {
120
+ this.runtime = runtime;
121
+ }
122
+ runtime;
123
+ get agentId() {
124
+ return this.runtime.agentId;
125
+ }
126
+ // ---- Triage entries ----
127
+ async storeTriage(opts) {
128
+ const id = newId();
129
+ const now = isoNow();
130
+ await executeRawSql(
131
+ this.runtime,
132
+ `INSERT INTO app_inbox.life_inbox_triage_entries (
133
+ id, agent_id, source, source_room_id, source_entity_id, source_message_id,
134
+ channel_name, channel_type, deep_link, classification, urgency, confidence,
135
+ snippet, sender_name, thread_context, triage_reasoning, suggested_response,
136
+ auto_replied, resolved, created_at, updated_at
137
+ ) VALUES (
138
+ ${sqlText(id)}, ${sqlText(this.agentId)}, ${sqlText(opts.source)},
139
+ ${sqlText(opts.sourceRoomId ?? null)}, ${sqlText(opts.sourceEntityId ?? null)},
140
+ ${sqlText(opts.sourceMessageId ?? null)}, ${sqlText(opts.channelName)},
141
+ ${sqlText(opts.channelType)}, ${sqlText(opts.deepLink ?? null)},
142
+ ${sqlText(opts.classification)}, ${sqlText(opts.urgency)},
143
+ ${sqlNumber(opts.confidence)}, ${sqlText(opts.snippet)},
144
+ ${sqlText(opts.senderName ?? null)}, ${sqlJsonArray(opts.threadContext)},
145
+ ${sqlText(opts.triageReasoning ?? null)}, ${sqlText(opts.suggestedResponse ?? null)},
146
+ FALSE, FALSE, ${sqlText(now)}, ${sqlText(now)}
147
+ )`
148
+ );
149
+ return {
150
+ id,
151
+ agentId: this.agentId,
152
+ source: opts.source,
153
+ sourceRoomId: opts.sourceRoomId ?? null,
154
+ sourceEntityId: opts.sourceEntityId ?? null,
155
+ sourceMessageId: opts.sourceMessageId ?? null,
156
+ channelName: opts.channelName,
157
+ channelType: opts.channelType,
158
+ deepLink: opts.deepLink ?? null,
159
+ classification: opts.classification,
160
+ urgency: opts.urgency,
161
+ confidence: opts.confidence,
162
+ snippet: opts.snippet,
163
+ senderName: opts.senderName ?? null,
164
+ threadContext: opts.threadContext ?? null,
165
+ triageReasoning: opts.triageReasoning ?? null,
166
+ suggestedResponse: opts.suggestedResponse ?? null,
167
+ draftResponse: null,
168
+ autoReplied: false,
169
+ resolved: false,
170
+ resolvedAt: null,
171
+ createdAt: now,
172
+ updatedAt: now
173
+ };
174
+ }
175
+ async getUnresolved(opts) {
176
+ const limit = opts?.limit ?? 50;
177
+ const rows = await executeRawSql(
178
+ this.runtime,
179
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
180
+ WHERE agent_id = ${sqlText(this.agentId)}
181
+ AND resolved = FALSE
182
+ ORDER BY
183
+ CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,
184
+ created_at DESC
185
+ LIMIT ${limit}`
186
+ );
187
+ return rows.map(parseTriageEntry);
188
+ }
189
+ async getUnresolvedForSender(opts) {
190
+ const limit = opts.limit ?? 50;
191
+ const clauses = [`agent_id = ${sqlText(this.agentId)}`, "resolved = FALSE"];
192
+ if (opts.excludeSource) {
193
+ clauses.push(`source != ${sqlText(opts.excludeSource)}`);
194
+ }
195
+ const senderClauses = [];
196
+ if (opts.sourceEntityId) {
197
+ senderClauses.push(`source_entity_id = ${sqlText(opts.sourceEntityId)}`);
198
+ }
199
+ if (opts.senderName) {
200
+ const normalized = opts.senderName.trim().toLowerCase();
201
+ if (normalized) {
202
+ senderClauses.push(
203
+ `(LOWER(sender_name) LIKE ${sqlText(`%${normalized}%`)} OR ${sqlText(normalized)} LIKE '%' || LOWER(sender_name) || '%')`
204
+ );
205
+ }
206
+ }
207
+ if (senderClauses.length === 0) return [];
208
+ clauses.push(`(${senderClauses.join(" OR ")})`);
209
+ const rows = await executeRawSql(
210
+ this.runtime,
211
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
212
+ WHERE ${clauses.join("\n AND ")}
213
+ ORDER BY
214
+ CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,
215
+ created_at DESC
216
+ LIMIT ${limit}`
217
+ );
218
+ return rows.map(parseTriageEntry);
219
+ }
220
+ async getByClassification(classification, opts) {
221
+ const limit = opts?.limit ?? 50;
222
+ const unresolvedOnly = opts?.unresolvedOnly !== false;
223
+ const resolvedClause = unresolvedOnly ? "AND resolved = FALSE" : "";
224
+ const rows = await executeRawSql(
225
+ this.runtime,
226
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
227
+ WHERE agent_id = ${sqlText(this.agentId)}
228
+ AND classification = ${sqlText(classification)}
229
+ ${resolvedClause}
230
+ ORDER BY created_at DESC
231
+ LIMIT ${limit}`
232
+ );
233
+ return rows.map(parseTriageEntry);
234
+ }
235
+ async getById(id) {
236
+ const rows = await executeRawSql(
237
+ this.runtime,
238
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
239
+ WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}
240
+ LIMIT 1`
241
+ );
242
+ const row = rows[0];
243
+ return row ? parseTriageEntry(row) : null;
244
+ }
245
+ async getBySourceMessageId(sourceMessageId) {
246
+ const rows = await executeRawSql(
247
+ this.runtime,
248
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
249
+ WHERE source_message_id = ${sqlText(sourceMessageId)}
250
+ AND agent_id = ${sqlText(this.agentId)}
251
+ LIMIT 1`
252
+ );
253
+ const row = rows[0];
254
+ return row ? parseTriageEntry(row) : null;
255
+ }
256
+ async getBySourceMessageIds(sourceMessageIds) {
257
+ if (sourceMessageIds.length === 0) return /* @__PURE__ */ new Set();
258
+ const inClause = sourceMessageIds.map((id) => sqlText(id)).join(", ");
259
+ const rows = await executeRawSql(
260
+ this.runtime,
261
+ `SELECT source_message_id FROM app_inbox.life_inbox_triage_entries
262
+ WHERE agent_id = ${sqlText(this.agentId)}
263
+ AND source_message_id IN (${inClause})`
264
+ );
265
+ return new Set(rows.map((r) => toText(r.source_message_id)));
266
+ }
267
+ async markResolved(id, opts) {
268
+ const now = isoNow();
269
+ const sets = [
270
+ `resolved = TRUE`,
271
+ `resolved_at = ${sqlText(now)}`,
272
+ `updated_at = ${sqlText(now)}`
273
+ ];
274
+ if (opts?.draftResponse !== void 0) {
275
+ sets.push(`draft_response = ${sqlText(opts.draftResponse)}`);
276
+ }
277
+ if (opts?.autoReplied !== void 0) {
278
+ sets.push(`auto_replied = ${sqlBoolean(opts.autoReplied)}`);
279
+ }
280
+ await executeRawSql(
281
+ this.runtime,
282
+ `UPDATE app_inbox.life_inbox_triage_entries
283
+ SET ${sets.join(", ")}
284
+ WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}`
285
+ );
286
+ }
287
+ async getRecentForDigest(sinceIso) {
288
+ const rows = await executeRawSql(
289
+ this.runtime,
290
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
291
+ WHERE agent_id = ${sqlText(this.agentId)}
292
+ AND created_at >= ${sqlText(sinceIso)}
293
+ AND classification != 'ignore'
294
+ ORDER BY
295
+ CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,
296
+ created_at DESC`
297
+ );
298
+ return rows.map(parseTriageEntry);
299
+ }
300
+ async getRecentAutoReplies(limit = 5) {
301
+ const rows = await executeRawSql(
302
+ this.runtime,
303
+ `SELECT * FROM app_inbox.life_inbox_triage_entries
304
+ WHERE agent_id = ${sqlText(this.agentId)}
305
+ AND auto_replied = TRUE
306
+ ORDER BY created_at DESC
307
+ LIMIT ${limit}`
308
+ );
309
+ return rows.map(parseTriageEntry);
310
+ }
311
+ async countAutoRepliesSince(sinceIso) {
312
+ const rows = await executeRawSql(
313
+ this.runtime,
314
+ `SELECT COUNT(*) AS cnt FROM app_inbox.life_inbox_triage_entries
315
+ WHERE agent_id = ${sqlText(this.agentId)}
316
+ AND auto_replied = TRUE
317
+ AND created_at >= ${sqlText(sinceIso)}`
318
+ );
319
+ return toNumber(rows[0]?.cnt, 0);
320
+ }
321
+ async cleanupOlderThan(olderThanIso) {
322
+ const rows = await executeRawSql(
323
+ this.runtime,
324
+ `DELETE FROM app_inbox.life_inbox_triage_entries
325
+ WHERE agent_id = ${sqlText(this.agentId)}
326
+ AND resolved = TRUE
327
+ AND created_at < ${sqlText(olderThanIso)}
328
+ RETURNING id`
329
+ );
330
+ return rows.length;
331
+ }
332
+ // ---- Few-shot examples ----
333
+ async storeExample(opts) {
334
+ const id = newId();
335
+ const now = isoNow();
336
+ const contextJson = opts.contextJson ?? {};
337
+ const contextStr = JSON.stringify(contextJson);
338
+ await executeRawSql(
339
+ this.runtime,
340
+ `INSERT INTO app_inbox.life_inbox_triage_examples (
341
+ id, agent_id, source, snippet, classification, owner_action,
342
+ owner_classification, context_json, created_at
343
+ ) VALUES (
344
+ ${sqlText(id)}, ${sqlText(this.agentId)}, ${sqlText(opts.source)},
345
+ ${sqlText(opts.snippet)}, ${sqlText(opts.classification)},
346
+ ${sqlText(opts.ownerAction)}, ${sqlText(opts.ownerClassification ?? null)},
347
+ ${sqlText(contextStr)}, ${sqlText(now)}
348
+ )`
349
+ );
350
+ return {
351
+ id,
352
+ agentId: this.agentId,
353
+ source: opts.source,
354
+ snippet: opts.snippet,
355
+ classification: opts.classification,
356
+ ownerAction: opts.ownerAction,
357
+ ownerClassification: opts.ownerClassification ?? null,
358
+ contextJson,
359
+ createdAt: now
360
+ };
361
+ }
362
+ async getExamples(limit = 10) {
363
+ const rows = await executeRawSql(
364
+ this.runtime,
365
+ `SELECT * FROM app_inbox.life_inbox_triage_examples
366
+ WHERE agent_id = ${sqlText(this.agentId)}
367
+ ORDER BY created_at DESC
368
+ LIMIT ${limit}`
369
+ );
370
+ return rows.map(parseTriageExample);
371
+ }
372
+ }
373
+ export {
374
+ InboxRepository
375
+ };
376
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/inbox/repository.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport {\n executeRawSql,\n parseJsonArray,\n parseJsonRecord,\n sqlBoolean,\n sqlNumber,\n sqlQuote,\n sqlText,\n toBoolean,\n toNumber,\n toText,\n} from \"../db/sql.js\";\nimport type {\n OwnerAction,\n TriageClassification,\n TriageEntry,\n TriageExample,\n TriageUrgency,\n} from \"./types.js\";\n\nconst TRIAGE_CLASSIFICATIONS = new Set<TriageClassification>([\n \"ignore\",\n \"info\",\n \"notify\",\n \"needs_reply\",\n \"urgent\",\n]);\n\nconst TRIAGE_URGENCIES = new Set<TriageUrgency>([\"low\", \"medium\", \"high\"]);\n\nconst OWNER_ACTIONS = new Set<OwnerAction>([\n \"confirmed\",\n \"reclassified\",\n \"edited_draft\",\n \"ignored\",\n]);\n\n// ---------------------------------------------------------------------------\n// Row parsing\n// ---------------------------------------------------------------------------\n\nfunction parseTriageClassification(value: unknown): TriageClassification {\n const normalized = toText(value).trim();\n if (TRIAGE_CLASSIFICATIONS.has(normalized as TriageClassification)) {\n return normalized as TriageClassification;\n }\n throw new Error(\n `[InboxRepository] invalid triage classification: ${normalized}`,\n );\n}\n\nfunction parseTriageUrgency(value: unknown): TriageUrgency {\n const normalized = toText(value).trim();\n if (TRIAGE_URGENCIES.has(normalized as TriageUrgency)) {\n return normalized as TriageUrgency;\n }\n throw new Error(`[InboxRepository] invalid triage urgency: ${normalized}`);\n}\n\nfunction parseNullableTriageClassification(\n value: unknown,\n): TriageClassification | null {\n const normalized = toText(value).trim();\n return normalized ? parseTriageClassification(normalized) : null;\n}\n\nfunction parseOwnerAction(value: unknown): OwnerAction {\n const normalized = toText(value).trim();\n if (OWNER_ACTIONS.has(normalized as OwnerAction)) {\n return normalized as OwnerAction;\n }\n throw new Error(`[InboxRepository] invalid owner action: ${normalized}`);\n}\n\nfunction parseJsonStringArray(value: unknown, label: string): string[] {\n const entries = parseJsonArray<unknown>(value);\n const strings: string[] = [];\n for (const entry of entries) {\n if (typeof entry !== \"string\") {\n throw new Error(`[InboxRepository] ${label} must contain strings`);\n }\n strings.push(entry);\n }\n return strings;\n}\n\nfunction parseTriageEntry(row: Record<string, unknown>): TriageEntry {\n return {\n id: toText(row.id),\n agentId: toText(row.agent_id),\n source: toText(row.source),\n sourceRoomId: toText(row.source_room_id) || null,\n sourceEntityId: toText(row.source_entity_id) || null,\n sourceMessageId: toText(row.source_message_id) || null,\n channelName: toText(row.channel_name),\n channelType: toText(row.channel_type),\n deepLink: toText(row.deep_link) || null,\n classification: parseTriageClassification(row.classification),\n urgency: parseTriageUrgency(row.urgency),\n confidence: toNumber(row.confidence, 0.5),\n snippet: toText(row.snippet),\n senderName: toText(row.sender_name) || null,\n threadContext: row.thread_context\n ? parseJsonStringArray(row.thread_context, \"thread_context\")\n : null,\n triageReasoning: toText(row.triage_reasoning) || null,\n suggestedResponse: toText(row.suggested_response) || null,\n draftResponse: toText(row.draft_response) || null,\n autoReplied: toBoolean(row.auto_replied, false),\n resolved: toBoolean(row.resolved, false),\n resolvedAt: toText(row.resolved_at) || null,\n createdAt: toText(row.created_at),\n updatedAt: toText(row.updated_at),\n };\n}\n\nfunction parseTriageExample(row: Record<string, unknown>): TriageExample {\n return {\n id: toText(row.id),\n agentId: toText(row.agent_id),\n source: toText(row.source),\n snippet: toText(row.snippet),\n classification: parseTriageClassification(row.classification),\n ownerAction: parseOwnerAction(row.owner_action),\n ownerClassification: parseNullableTriageClassification(\n row.owner_classification,\n ),\n contextJson: parseJsonRecord(row.context_json),\n createdAt: toText(row.created_at),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction newId(): string {\n return crypto.randomUUID();\n}\n\nfunction isoNow(): string {\n return new Date().toISOString();\n}\n\nfunction sqlJsonArray(value: string[] | null | undefined): string {\n if (!value || value.length === 0) return \"NULL\";\n return sqlQuote(JSON.stringify(value));\n}\n\n// ---------------------------------------------------------------------------\n// Repository\n//\n// Reads/writes the cross-channel triage tables that remain registered by\n// @elizaos/plugin-personal-assistant (`app_inbox.life_inbox_triage_entries`\n// and `_examples`). The tables are inbox-domain but PA owns their migration; we\n// read them directly via the runtime DB handle so this plugin carries no PA\n// dependency. See README for the schema-ownership decision.\n// ---------------------------------------------------------------------------\n\nexport class InboxRepository {\n constructor(private runtime: IAgentRuntime) {}\n\n private get agentId(): string {\n return this.runtime.agentId;\n }\n\n // ---- Triage entries ----\n\n async storeTriage(opts: {\n source: string;\n sourceRoomId?: string;\n sourceEntityId?: string;\n sourceMessageId?: string;\n channelName: string;\n channelType: string;\n deepLink?: string;\n classification: TriageClassification;\n urgency: TriageUrgency;\n confidence: number;\n snippet: string;\n senderName?: string;\n threadContext?: string[];\n triageReasoning?: string;\n suggestedResponse?: string;\n }): Promise<TriageEntry> {\n const id = newId();\n const now = isoNow();\n\n await executeRawSql(\n this.runtime,\n `INSERT INTO app_inbox.life_inbox_triage_entries (\n id, agent_id, source, source_room_id, source_entity_id, source_message_id,\n channel_name, channel_type, deep_link, classification, urgency, confidence,\n snippet, sender_name, thread_context, triage_reasoning, suggested_response,\n auto_replied, resolved, created_at, updated_at\n ) VALUES (\n ${sqlText(id)}, ${sqlText(this.agentId)}, ${sqlText(opts.source)},\n ${sqlText(opts.sourceRoomId ?? null)}, ${sqlText(opts.sourceEntityId ?? null)},\n ${sqlText(opts.sourceMessageId ?? null)}, ${sqlText(opts.channelName)},\n ${sqlText(opts.channelType)}, ${sqlText(opts.deepLink ?? null)},\n ${sqlText(opts.classification)}, ${sqlText(opts.urgency)},\n ${sqlNumber(opts.confidence)}, ${sqlText(opts.snippet)},\n ${sqlText(opts.senderName ?? null)}, ${sqlJsonArray(opts.threadContext)},\n ${sqlText(opts.triageReasoning ?? null)}, ${sqlText(opts.suggestedResponse ?? null)},\n FALSE, FALSE, ${sqlText(now)}, ${sqlText(now)}\n )`,\n );\n\n return {\n id,\n agentId: this.agentId,\n source: opts.source,\n sourceRoomId: opts.sourceRoomId ?? null,\n sourceEntityId: opts.sourceEntityId ?? null,\n sourceMessageId: opts.sourceMessageId ?? null,\n channelName: opts.channelName,\n channelType: opts.channelType,\n deepLink: opts.deepLink ?? null,\n classification: opts.classification,\n urgency: opts.urgency,\n confidence: opts.confidence,\n snippet: opts.snippet,\n senderName: opts.senderName ?? null,\n threadContext: opts.threadContext ?? null,\n triageReasoning: opts.triageReasoning ?? null,\n suggestedResponse: opts.suggestedResponse ?? null,\n draftResponse: null,\n autoReplied: false,\n resolved: false,\n resolvedAt: null,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n async getUnresolved(opts?: { limit?: number }): Promise<TriageEntry[]> {\n const limit = opts?.limit ?? 50;\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND resolved = FALSE\n ORDER BY\n CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,\n created_at DESC\n LIMIT ${limit}`,\n );\n return rows.map(parseTriageEntry);\n }\n\n async getUnresolvedForSender(opts: {\n sourceEntityId?: string | null;\n senderName?: string | null;\n excludeSource?: string | null;\n limit?: number;\n }): Promise<TriageEntry[]> {\n const limit = opts.limit ?? 50;\n const clauses = [`agent_id = ${sqlText(this.agentId)}`, \"resolved = FALSE\"];\n\n if (opts.excludeSource) {\n clauses.push(`source != ${sqlText(opts.excludeSource)}`);\n }\n\n const senderClauses: string[] = [];\n if (opts.sourceEntityId) {\n senderClauses.push(`source_entity_id = ${sqlText(opts.sourceEntityId)}`);\n }\n if (opts.senderName) {\n const normalized = opts.senderName.trim().toLowerCase();\n if (normalized) {\n senderClauses.push(\n `(LOWER(sender_name) LIKE ${sqlText(`%${normalized}%`)} OR ${sqlText(normalized)} LIKE '%' || LOWER(sender_name) || '%')`,\n );\n }\n }\n\n if (senderClauses.length === 0) return [];\n clauses.push(`(${senderClauses.join(\" OR \")})`);\n\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE ${clauses.join(\"\\n AND \")}\n ORDER BY\n CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,\n created_at DESC\n LIMIT ${limit}`,\n );\n return rows.map(parseTriageEntry);\n }\n\n async getByClassification(\n classification: TriageClassification,\n opts?: { limit?: number; unresolvedOnly?: boolean },\n ): Promise<TriageEntry[]> {\n const limit = opts?.limit ?? 50;\n const unresolvedOnly = opts?.unresolvedOnly !== false;\n const resolvedClause = unresolvedOnly ? \"AND resolved = FALSE\" : \"\";\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND classification = ${sqlText(classification)}\n ${resolvedClause}\n ORDER BY created_at DESC\n LIMIT ${limit}`,\n );\n return rows.map(parseTriageEntry);\n }\n\n async getById(id: string): Promise<TriageEntry | null> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}\n LIMIT 1`,\n );\n const row = rows[0];\n return row ? parseTriageEntry(row) : null;\n }\n\n async getBySourceMessageId(\n sourceMessageId: string,\n ): Promise<TriageEntry | null> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE source_message_id = ${sqlText(sourceMessageId)}\n AND agent_id = ${sqlText(this.agentId)}\n LIMIT 1`,\n );\n const row = rows[0];\n return row ? parseTriageEntry(row) : null;\n }\n\n async getBySourceMessageIds(\n sourceMessageIds: string[],\n ): Promise<Set<string>> {\n if (sourceMessageIds.length === 0) return new Set();\n const inClause = sourceMessageIds.map((id) => sqlText(id)).join(\", \");\n const rows = await executeRawSql(\n this.runtime,\n `SELECT source_message_id FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND source_message_id IN (${inClause})`,\n );\n return new Set(rows.map((r) => toText(r.source_message_id)));\n }\n\n async markResolved(\n id: string,\n opts?: { draftResponse?: string; autoReplied?: boolean },\n ): Promise<void> {\n const now = isoNow();\n const sets = [\n `resolved = TRUE`,\n `resolved_at = ${sqlText(now)}`,\n `updated_at = ${sqlText(now)}`,\n ];\n if (opts?.draftResponse !== undefined) {\n sets.push(`draft_response = ${sqlText(opts.draftResponse)}`);\n }\n if (opts?.autoReplied !== undefined) {\n sets.push(`auto_replied = ${sqlBoolean(opts.autoReplied)}`);\n }\n await executeRawSql(\n this.runtime,\n `UPDATE app_inbox.life_inbox_triage_entries\n SET ${sets.join(\", \")}\n WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}`,\n );\n }\n\n async getRecentForDigest(sinceIso: string): Promise<TriageEntry[]> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND created_at >= ${sqlText(sinceIso)}\n AND classification != 'ignore'\n ORDER BY\n CASE urgency WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END,\n created_at DESC`,\n );\n return rows.map(parseTriageEntry);\n }\n\n async getRecentAutoReplies(limit = 5): Promise<TriageEntry[]> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND auto_replied = TRUE\n ORDER BY created_at DESC\n LIMIT ${limit}`,\n );\n return rows.map(parseTriageEntry);\n }\n\n async countAutoRepliesSince(sinceIso: string): Promise<number> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT COUNT(*) AS cnt FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND auto_replied = TRUE\n AND created_at >= ${sqlText(sinceIso)}`,\n );\n return toNumber(rows[0]?.cnt, 0);\n }\n\n async cleanupOlderThan(olderThanIso: string): Promise<number> {\n const rows = await executeRawSql(\n this.runtime,\n `DELETE FROM app_inbox.life_inbox_triage_entries\n WHERE agent_id = ${sqlText(this.agentId)}\n AND resolved = TRUE\n AND created_at < ${sqlText(olderThanIso)}\n RETURNING id`,\n );\n return rows.length;\n }\n\n // ---- Few-shot examples ----\n\n async storeExample(opts: {\n source: string;\n snippet: string;\n classification: TriageClassification;\n ownerAction: OwnerAction;\n ownerClassification?: TriageClassification;\n contextJson?: Record<string, unknown>;\n }): Promise<TriageExample> {\n const id = newId();\n const now = isoNow();\n const contextJson = opts.contextJson ?? {};\n const contextStr = JSON.stringify(contextJson);\n\n await executeRawSql(\n this.runtime,\n `INSERT INTO app_inbox.life_inbox_triage_examples (\n id, agent_id, source, snippet, classification, owner_action,\n owner_classification, context_json, created_at\n ) VALUES (\n ${sqlText(id)}, ${sqlText(this.agentId)}, ${sqlText(opts.source)},\n ${sqlText(opts.snippet)}, ${sqlText(opts.classification)},\n ${sqlText(opts.ownerAction)}, ${sqlText(opts.ownerClassification ?? null)},\n ${sqlText(contextStr)}, ${sqlText(now)}\n )`,\n );\n\n return {\n id,\n agentId: this.agentId,\n source: opts.source,\n snippet: opts.snippet,\n classification: opts.classification,\n ownerAction: opts.ownerAction,\n ownerClassification: opts.ownerClassification ?? null,\n contextJson,\n createdAt: now,\n };\n }\n\n async getExamples(limit = 10): Promise<TriageExample[]> {\n const rows = await executeRawSql(\n this.runtime,\n `SELECT * FROM app_inbox.life_inbox_triage_examples\n WHERE agent_id = ${sqlText(this.agentId)}\n ORDER BY created_at DESC\n LIMIT ${limit}`,\n );\n return rows.map(parseTriageExample);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * InboxService — the inbox triage back-end.
3
+ *
4
+ * Standalone successor to the inbox-domain logic that lived in PA's
5
+ * `service-mixin-inbox` + `inbox/` modules. It holds its own runtime and
6
+ * {@link InboxRepository} (raw SQL over the `app_inbox.life_inbox_triage_*`
7
+ * tables PA still registers), classifies inbound messages with the LLM, and
8
+ * answers the triage-queue reads the INBOX action and inboxTriage provider need.
9
+ * It carries no dependency on `@elizaos/plugin-personal-assistant`.
10
+ *
11
+ * NOT here (delegated / left in PA, by design):
12
+ * - `getInbox` / `markInboxEntryRead` — the cached cross-channel inbox that
13
+ * backs `GET /api/lifeops/inbox`. It is coupled to PA's `LifeOpsRepository`
14
+ * inbox cache, LLM priority scoring, Gmail/X connector sources, and the
15
+ * app-state store, so it remains a PA service method (the route shape stays
16
+ * byte-identical). InboxService takes the inbound feed as input instead of
17
+ * pulling connectors itself.
18
+ */
19
+ import type { IAgentRuntime } from "@elizaos/core";
20
+ import { type CurationDecision, type EmailCurationIdentityHook, type EmailCurationOutput, type EmailCurationPolicy, type EmailCurationPolicyHook } from "./email-curation.js";
21
+ import { InboxRepository } from "./repository.js";
22
+ import type { InboundMessage, InboxTriageConfig, TriageClassification, TriageEntry } from "./types.js";
23
+ export interface TriageOptions {
24
+ /** Override the loaded triage config (priority senders/channels, rules). */
25
+ config?: InboxTriageConfig;
26
+ /** Owner context string injected into the classifier prompt. */
27
+ ownerContext?: string;
28
+ /** How many past owner-corrected examples to few-shot the classifier with. */
29
+ exampleLimit?: number;
30
+ /** Skip persistence and only return the classification (default false). */
31
+ classifyOnly?: boolean;
32
+ }
33
+ export interface TriagedMessage {
34
+ message: InboundMessage;
35
+ classification: TriageClassification;
36
+ urgency: "low" | "medium" | "high";
37
+ confidence: number;
38
+ reasoning: string;
39
+ suggestedResponse?: string;
40
+ /** The persisted triage entry, unless `classifyOnly` was set. */
41
+ entry?: TriageEntry;
42
+ }
43
+ export interface TriageRunResult {
44
+ triaged: TriagedMessage[];
45
+ }
46
+ export interface SearchOptions {
47
+ classification?: TriageClassification;
48
+ limit?: number;
49
+ unresolvedOnly?: boolean;
50
+ }
51
+ export interface CurateOptions {
52
+ /**
53
+ * Identity resolver. Defaults to a hook backed by the runtime
54
+ * {@link resolveKnowledgeGraphService | knowledge-graph service}, which
55
+ * resolves the sender's entity from the runtime graph. Injectable as a test
56
+ * seam and to let callers override the identity source.
57
+ */
58
+ identityHook?: EmailCurationIdentityHook;
59
+ /**
60
+ * Policy hook applied after the engine's provisional decision. Defaults to a
61
+ * no-op (the engine's built-in `DEFAULT_POLICY` thresholds and delete
62
+ * blockers still apply). No PA-owned policy store is reachable from the
63
+ * inbox plugin without importing PA, so there is no default policy source
64
+ * beyond the engine's own defaults.
65
+ */
66
+ policyHook?: EmailCurationPolicyHook;
67
+ /** Static policy overrides merged onto the engine defaults. */
68
+ policy?: EmailCurationPolicy;
69
+ /** Override the curation timestamp (defaults to engine `now`). */
70
+ now?: string;
71
+ }
72
+ /** The curation decision attached to a triaged message. */
73
+ export interface CuratedMessage extends TriagedMessage {
74
+ curation: CurationDecision;
75
+ }
76
+ export interface TriageWithCurationResult {
77
+ triaged: CuratedMessage[];
78
+ curation: EmailCurationOutput;
79
+ }
80
+ /**
81
+ * The triage / search / list back-end for the inbox domain. One instance per
82
+ * call is fine — the repository is a thin raw-SQL wrapper over the runtime DB.
83
+ */
84
+ export declare class InboxService {
85
+ private readonly runtime;
86
+ private readonly repository;
87
+ constructor(runtime: IAgentRuntime);
88
+ getRepository(): InboxRepository;
89
+ /**
90
+ * Classify a batch of inbound messages and (unless `classifyOnly`) persist
91
+ * one triage entry per message. Returns the per-message decision in input
92
+ * order. Messages already triaged by `source_message_id` are skipped so a
93
+ * re-run does not double-store.
94
+ */
95
+ triage(messages: InboundMessage[], opts?: TriageOptions): Promise<TriageRunResult>;
96
+ /**
97
+ * Surface an act-now triage entry (`urgent` / `needs_reply`) on the home
98
+ * notification rail. Best-effort: a runtime with no NotificationService (a
99
+ * headless/test runtime) is a no-op. `info`/`notify`/`ignore` stay in the
100
+ * inbox view without pushing a notification.
101
+ */
102
+ private notifyAttention;
103
+ /**
104
+ * Run the pure email-curation engine over a batch of inbound messages and
105
+ * return the per-candidate decision (save / archive / delete / review with
106
+ * evidence, citations, and a bulk-review block).
107
+ *
108
+ * This is the richer decision path that complements {@link triage}: triage
109
+ * answers "how urgent is this and should I reply", curation answers "what
110
+ * should happen to this message in the owner's mailbox". It does not persist
111
+ * anything — callers decide what to do with the suggested action.
112
+ *
113
+ * The identity hook is backed by the runtime knowledge-graph service so the
114
+ * sender's entity (VIP / known person / service) feeds the engine's
115
+ * delete-blockers. Both hooks are injectable as a test seam.
116
+ */
117
+ curate(messages: InboundMessage[], opts?: CurateOptions): Promise<EmailCurationOutput>;
118
+ /**
119
+ * Triage a batch, then attach a curation decision to each message in the
120
+ * same input order. Triage behavior is unchanged (the existing
121
+ * classification + persistence still runs); curation is additive.
122
+ */
123
+ triageWithCuration(messages: InboundMessage[], opts?: TriageOptions & CurateOptions): Promise<TriageWithCurationResult>;
124
+ /**
125
+ * Build an {@link EmailCurationIdentityHook} backed by the runtime
126
+ * knowledge-graph service. Pre-resolves every candidate's sender against the
127
+ * entity graph (the engine hook itself must be synchronous, so the async
128
+ * graph reads happen here) and maps the resolved entity onto the engine's
129
+ * identity kinds. Candidates the graph cannot resolve fall through to the
130
+ * engine's built-in sender heuristics. When the graph service is absent the
131
+ * hook resolves nothing.
132
+ */
133
+ private buildKnowledgeGraphIdentityHook;
134
+ /**
135
+ * Read persisted triage entries, optionally filtered by classification.
136
+ * Backs the INBOX action's `search`/`list` reads over the triage queue.
137
+ */
138
+ search(opts?: SearchOptions): Promise<TriageEntry[]>;
139
+ /** Unresolved triage queue (urgency-ordered). */
140
+ list(limit?: number): Promise<TriageEntry[]>;
141
+ /** Non-ignored triage entries created since `sinceIso`, urgency-ordered. */
142
+ digest(sinceIso: string): Promise<TriageEntry[]>;
143
+ /** Mark a triage entry resolved (optionally recording the sent draft). */
144
+ resolve(id: string, opts?: {
145
+ draftResponse?: string;
146
+ autoReplied?: boolean;
147
+ }): Promise<void>;
148
+ }
149
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/inbox/service.ts"],"names":[],"mappings":"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"}