@happyvertical/smrt-messages 0.55.4 → 0.55.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 (26) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/chunks/{AccountCollection-LHFMPeF7.js → AccountCollection-D0jYNESQ.js} +2 -2
  3. package/dist/chunks/{AccountCollection-LHFMPeF7.js.map → AccountCollection-D0jYNESQ.js.map} +1 -1
  4. package/dist/chunks/{Email-tafppJZ4.js → Email-hneqk2_l.js} +3 -3
  5. package/dist/chunks/{Email-tafppJZ4.js.map → Email-hneqk2_l.js.map} +1 -1
  6. package/dist/chunks/{EmailFolder-CLahyNFX.js → EmailFolder-CFd0haPW.js} +2 -2
  7. package/dist/chunks/{EmailFolder-CLahyNFX.js.map → EmailFolder-CFd0haPW.js.map} +1 -1
  8. package/dist/chunks/{Message-DZ1al6M1.js → Message-CSv9rJva.js} +4 -4
  9. package/dist/chunks/{Message-DZ1al6M1.js.map → Message-CSv9rJva.js.map} +1 -1
  10. package/dist/chunks/{MessageCollection-8eaZgS4_.js → MessageCollection-4yzuqVl4.js} +2 -2
  11. package/dist/chunks/{MessageCollection-8eaZgS4_.js.map → MessageCollection-4yzuqVl4.js.map} +1 -1
  12. package/dist/chunks/__smrt-register__-DyB0ALdL.js +6 -0
  13. package/dist/chunks/__smrt-register__-DyB0ALdL.js.map +1 -0
  14. package/dist/chunks/intake-snapshot-B03bDtSW.js +55 -0
  15. package/dist/chunks/intake-snapshot-B03bDtSW.js.map +1 -0
  16. package/dist/index.js +31 -9
  17. package/dist/index.js.map +1 -1
  18. package/dist/intake-snapshot.d.ts +27 -0
  19. package/dist/intake-snapshot.d.ts.map +1 -0
  20. package/dist/manifest.json +32 -1
  21. package/dist/models/EmailAccount.d.ts +10 -0
  22. package/dist/models/EmailAccount.d.ts.map +1 -1
  23. package/dist/smrt-knowledge.json +26 -5
  24. package/package.json +15 -15
  25. package/dist/chunks/__smrt-register__-DtlRO--l.js +0 -6
  26. package/dist/chunks/__smrt-register__-DtlRO--l.js.map +0 -1
@@ -0,0 +1,27 @@
1
+ import type { EmailMessage } from '@happyvertical/email';
2
+ /** Full-message byte snapshot for an already authorized, configured account. */
3
+ export interface EmailIntakeSnapshot {
4
+ accountId: string;
5
+ messageId: string;
6
+ threadId: string;
7
+ inReplyTo: string;
8
+ subject: string;
9
+ text: string;
10
+ html: string;
11
+ date: string;
12
+ attachments: Array<{
13
+ partId: string;
14
+ filename: string;
15
+ mediaType: string;
16
+ bytes: Uint8Array;
17
+ }>;
18
+ }
19
+ /** Safe machine-readable snapshot failure; never contains provider error details. */
20
+ export declare class EmailIntakeSnapshotError extends Error {
21
+ readonly category: 'invalid' | 'limit' | 'unavailable_bytes';
22
+ readonly name = "EmailIntakeSnapshotError";
23
+ constructor(category: 'invalid' | 'limit' | 'unavailable_bytes', message: string);
24
+ }
25
+ /** Copies actual provider bytes; missing bytes fail closed, never read provider-supplied paths. */
26
+ export declare function snapshotEmailForIntake(accountId: string, message: EmailMessage, maxBytes: number): EmailIntakeSnapshot;
27
+ //# sourceMappingURL=intake-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake-snapshot.d.ts","sourceRoot":"","sources":["../src/intake-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,KAAK,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,qFAAqF;AACrF,qBAAa,wBAAyB,SAAQ,KAAK;aAG/B,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,mBAAmB;IAFrE,SAAkB,IAAI,8BAA8B;gBAElC,QAAQ,EAAE,SAAS,GAAG,OAAO,GAAG,mBAAmB,EACnE,OAAO,EAAE,MAAM;CAIlB;AAED,mGAAmG;AACnG,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,GACf,mBAAmB,CAwDrB"}
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-messages",
5
- "packageVersion": "0.55.4",
5
+ "packageVersion": "0.55.7",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-messages:AccountCollection": {
8
8
  "name": "accountcollection",
@@ -4367,6 +4367,7 @@
4367
4367
  "className": "Blacklist",
4368
4368
  "qualifiedName": "@happyvertical/smrt-messages:Blacklist",
4369
4369
  "collection": "blacklists",
4370
+ "description": "Block entries for email filtering",
4370
4371
  "filePath": "/home/runner/work/smrt/smrt/packages/messages/src/models/Blacklist.ts",
4371
4372
  "packageName": "@happyvertical/smrt-messages",
4372
4373
  "fields": {
@@ -5667,6 +5668,34 @@
5667
5668
  "isStatic": false,
5668
5669
  "isPublic": true
5669
5670
  },
5671
+ "readIntakeMessage": {
5672
+ "name": "readIntakeMessage",
5673
+ "async": true,
5674
+ "parameters": [
5675
+ {
5676
+ "name": "locator",
5677
+ "type": "string",
5678
+ "optional": false
5679
+ },
5680
+ {
5681
+ "name": "maxBytes",
5682
+ "type": "number",
5683
+ "optional": false
5684
+ },
5685
+ {
5686
+ "name": "imapIdentity",
5687
+ "type": "object",
5688
+ "optional": true,
5689
+ "memberTypes": [
5690
+ "string",
5691
+ "number"
5692
+ ]
5693
+ }
5694
+ ],
5695
+ "returnType": "any",
5696
+ "isStatic": false,
5697
+ "isPublic": true
5698
+ },
5670
5699
  "syncFrom": {
5671
5700
  "name": "syncFrom",
5672
5701
  "async": true,
@@ -10428,6 +10457,7 @@
10428
10457
  "className": "UserNotification",
10429
10458
  "qualifiedName": "@happyvertical/smrt-messages:UserNotification",
10430
10459
  "collection": "usernotifications",
10460
+ "description": "One in-app notification for one person, with its own read state.",
10431
10461
  "filePath": "/home/runner/work/smrt/smrt/packages/messages/src/models/UserNotification.ts",
10432
10462
  "packageName": "@happyvertical/smrt-messages",
10433
10463
  "fields": {
@@ -10693,6 +10723,7 @@
10693
10723
  "className": "Whitelist",
10694
10724
  "qualifiedName": "@happyvertical/smrt-messages:Whitelist",
10695
10725
  "collection": "whitelists",
10726
+ "description": "Allow-only entries for email filtering",
10696
10727
  "filePath": "/home/runner/work/smrt/smrt/packages/messages/src/models/Whitelist.ts",
10697
10728
  "packageName": "@happyvertical/smrt-messages",
10698
10729
  "fields": {
@@ -35,6 +35,16 @@ export declare class EmailAccount extends Account {
35
35
  * '/providers/all') once during startup.
36
36
  */
37
37
  createClient(): Promise<import("@happyvertical/email").EmailClient>;
38
+ /**
39
+ * Read an immutable body/attachment-byte snapshot for an authorized intake host.
40
+ * Does not sync, acknowledge, mark read or advance mailbox checkpoints.
41
+ * Metadata-only providers fail explicitly; provider paths are never dereferenced.
42
+ */
43
+ readIntakeMessage(locator: string, maxBytes: number, imapIdentity?: {
44
+ folder: string;
45
+ uidValidity: number;
46
+ uid: string;
47
+ }): Promise<import("../intake-snapshot.js").EmailIntakeSnapshot>;
38
48
  /**
39
49
  * Sync emails from the email server to the database
40
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"EmailAccount.d.ts","sourceRoot":"","sources":["../../src/models/EmailAccount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,WAAW,EACX,UAAU,EACX,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,KAAK,SAAM;IACX,mBAAmB,SAAM;gBAEb,OAAO,GAAE,mBAAwB;IAW7C;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAO9D;;;;;;;;;;OAUG;IACG,YAAY;IAwBlB;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuL9D;;OAEG;IACG,UAAU;IAShB;;OAEG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM;IAc9B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAOvC;;OAEG;IACY,cAAc,CAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;OAOG;IACG,OAAO,CACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAU5D;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAcxC"}
1
+ {"version":3,"file":"EmailAccount.d.ts","sourceRoot":"","sources":["../../src/models/EmailAccount.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EAEtB,WAAW,EACX,UAAU,EACX,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAW,CAAC;AAEpC,qBAMa,YAAa,SAAQ,OAAO;IACvC,KAAK,SAAM;IACX,mBAAmB,SAAM;gBAEb,OAAO,GAAE,mBAAwB;IAW7C;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACY,YAAY,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAO9D;;;;;;;;;;OAUG;IACG,YAAY;IAwBlB;;;;OAIG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAkCrE;;OAEG;IACG,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAuL9D;;OAEG;IACG,UAAU;IAShB;;OAEG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM;IAc9B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAOvC;;OAEG;IACY,cAAc,CAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,GAAE;QACP,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACd,GACL,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;OAOG;IACG,OAAO,CACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;IAU5D;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAcxC"}
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-messages",
6
- "packageVersion": "0.55.4",
6
+ "packageVersion": "0.55.7",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "21b57622ac16453769255fe518d228b168510d24f960bdee31af3da0c2f6b9c3",
11
- "packageJson": "1a820de28783573c2cad9cf017412611d497c90a957ed0db416c10217d105c50",
12
- "agents": "36405a8a9cefd4f831381d3670572dc3489d43fbc1787fb12b70814cc1e59f34"
10
+ "manifest": "659c7f9d8ac03578b8941f7ed4d51bfd694d4d8935fdfc6fdc6ad2d4ba2c7ea0",
11
+ "packageJson": "a78822c0a563fc3c3f4f3814e7a513206b07e2a913fb6383acebc99a2a6fe50b",
12
+ "agents": "f2bbd8f6d74a68c2ab949153f2051c7a906bd2e4ba547873b3641d0779491c59"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -4563,6 +4563,7 @@
4563
4563
  "name": "Blacklist",
4564
4564
  "qualifiedName": "@happyvertical/smrt-messages:Blacklist",
4565
4565
  "collection": "blacklists",
4566
+ "description": "Block entries for email filtering",
4566
4567
  "tableName": "blacklists",
4567
4568
  "packageName": "@happyvertical/smrt-messages",
4568
4569
  "extends": "SmrtObject",
@@ -5814,6 +5815,7 @@
5814
5815
  "getSettings",
5815
5816
  "getUnreadCount",
5816
5817
  "migrateToSecrets",
5818
+ "readIntakeMessage",
5817
5819
  "setConfiguration",
5818
5820
  "setCredentials",
5819
5821
  "setSettings",
@@ -5877,6 +5879,16 @@
5877
5879
  "async": true,
5878
5880
  "returns": "Promise<void>"
5879
5881
  },
5882
+ {
5883
+ "name": "readIntakeMessage",
5884
+ "async": true,
5885
+ "params": [
5886
+ "locator: string",
5887
+ "maxBytes: number",
5888
+ "imapIdentity?: object"
5889
+ ],
5890
+ "returns": "any"
5891
+ },
5880
5892
  {
5881
5893
  "name": "setConfiguration",
5882
5894
  "params": [
@@ -6038,6 +6050,13 @@
6038
6050
  "reason": "not listed in api.include",
6039
6051
  "objectName": "@happyvertical/smrt-messages:EmailAccount"
6040
6052
  },
6053
+ {
6054
+ "kind": "api",
6055
+ "operation": "readIntakeMessage",
6056
+ "code": "not-included",
6057
+ "reason": "not listed in api.include",
6058
+ "objectName": "@happyvertical/smrt-messages:EmailAccount"
6059
+ },
6041
6060
  {
6042
6061
  "kind": "api",
6043
6062
  "operation": "setConfiguration",
@@ -10141,6 +10160,7 @@
10141
10160
  "qualifiedName": "@happyvertical/smrt-messages:UserNotification",
10142
10161
  "displayLabelField": "title",
10143
10162
  "collection": "usernotifications",
10163
+ "description": "One in-app notification for one person, with its own read state.",
10144
10164
  "tableName": "user_notifications",
10145
10165
  "packageName": "@happyvertical/smrt-messages",
10146
10166
  "extends": "SmrtObject",
@@ -10246,6 +10266,7 @@
10246
10266
  "name": "Whitelist",
10247
10267
  "qualifiedName": "@happyvertical/smrt-messages:Whitelist",
10248
10268
  "collection": "whitelists",
10269
+ "description": "Allow-only entries for email filtering",
10249
10270
  "tableName": "whitelists",
10250
10271
  "packageName": "@happyvertical/smrt-messages",
10251
10272
  "extends": "SmrtObject",
@@ -15138,5 +15159,5 @@
15138
15159
  "polymorphicAssociations": 0,
15139
15160
  "uuidColumns": 96
15140
15161
  },
15141
- "agentDoc": "# @happyvertical/smrt-messages\n\nMulti-channel messaging with STI hierarchies for both messages and accounts. Credential encryption via smrt-secrets.\n\n## STI Hierarchies\n\n**Messages** (share `messages` table):\n- `Message` (base): accountId, personaId, endpointId, correlationId, threadId, subject, body, fromAddress, toAddresses (JSON), sendStatus (draft/sending/sent/failed), retryCount\n- `Email`: messageId (RFC 822), inReplyTo, ccAddresses, bccAddresses, htmlBody, textBody, folderId, labels, headers\n- `Tweet`: tweetId, retweetCount, likeCount, mediaUrls, hashtags, mentions\n- `SlackMessage`: channelId, slackTs, slackThreadTs, reactions, blocks\n\n**Accounts** (share `accounts` table):\n- `Account` (base): channelType, providerType, public configuration,\n credentialSecretId, isActive, lastSyncAt, deprecated sensitive settings\n- `EmailAccount`, `TwitterAccount`, `SlackAccount`, `ZulipAccount`,\n `TelegramAccount`: provider-specific identity/sender behavior\n\n## Persona messaging\n\n- `MessagingEndpoint`: tenant-owned, write-only destination JSON with masked\n display helper.\n- `PersonaMessageRoute`: persona → account + endpoint, selected by purpose and\n descending priority.\n- `PersonaMessagingService`: persists and sends through an enabled route.\n- Provider registry: public setup descriptors + optional sender factory. Keep\n channel (`sms`) separate from provider (`twilio`).\n- Permissions: `messages.send`, `messages.manage-routes`,\n `messages.manage-credentials`.\n- `MessagingSettingsService` is the permissioned server boundary;\n its host-supplied `resolvePersonaTenantId` proves cross-package ownership.\n `MessagingSettingsPanel` is the callback-driven Svelte surface.\n- Account, endpoint, and route generated surfaces are read-only. Do not add\n generated mutations around the settings service or make secret fields writable.\n\n## In-app notifications (`UserNotification`)\n\nThe in-app channel: one stored row per person, with read state.\n`user_notifications`: `tenantId` (required), `recipientUserId`\n(`@crossPackageRef` smrt-users:User, uuid), `kind`, `title`, `body`, `href`,\n`severity` (info/success/warning/error), `sourceRef`, `occurredAt`, `readAt`,\n`dismissedAt`. Conflict identity `(tenant_id, recipient_user_id, source_ref)`.\n\n- `UserNotificationService` is the only access path: `notify()` /\n `notifyMany()` (dedupe on `sourceRef`, existing row returned untouched;\n a unique ref is generated when omitted), `listForUser()` / `countUnread()`\n across explicit `tenantIds` (each read under `withTenant`), `markRead()`,\n `dismiss()` (also marks read), `markAllRead()`. Every write filters on the\n recipient, so another user's id matches nothing.\n- Generated REST/MCP/CLI are off (`api`/`mcp`/`cli: false`): a tenant-scoped\n list would expose every member's rows. Hosts expose their own routes that\n pass the signed-in user's id, never a request-chosen one.\n- Hosts decide who is told (e.g. `SocialPost.createdByUserId` for a failed\n post). New table: consumers run `db:migrate`.\n\n## Credential Security\n\nAccount credentials stored via tenant-bound `credentialSecretId` →\nsmrt-secrets. Use `setCredentials()`/`getCredentials()` — never store passwords\ndirectly. Credential descriptor schemas contain field definitions, never values.\n\n## Send Lifecycle\n\n`message.send()`: resolves account → creates provider sender → updates sendStatus. Retry support with `maxRetries` budget.\n\n## Provider entry points (bundle boundary, #1979)\n\nThe package root is provider-neutral: models, collections, services,\npermissions, and the provider registry carry **zero provider-SDK weight**.\nProvider SDK wrappers (`@happyvertical/email`, `@happyvertical/messages`) are\nreachable only through explicit entry points, imported once at server startup:\n\n```ts\nimport '@happyvertical/smrt-messages/providers/email'; // smtp/imap/pop3/gmail\nimport '@happyvertical/smrt-messages/providers/slack';\nimport '@happyvertical/smrt-messages/providers/twitter';\nimport '@happyvertical/smrt-messages/providers/all'; // all of the above\n```\n\n- Without the matching entry, `EmailAccount.createClient()` (send + mailbox\n sync) throws an actionable error and `SlackSender`/`TweetSender` return a\n failure result naming the entry to import. Telegram and Zulip are\n fetch-based and always work from the root.\n- Builtin registry definitions stay metadata-only (setup fields for UIs); the\n entries upgrade them with `createEmailClient`/`createMessageClient`/\n `createSender` hooks. Never import a provider SDK from any module reachable\n from `src/index.ts` — the consumer bundle gate (`packages/bundle-gate`)\n fails CI when one becomes reachable.\n- `Attachment.readContent()` loads `@happyvertical/files` through core's\n `importOptionalDependency` boundary and keeps its null-on-failure contract\n in builds where the SDK is absent.\n\n## Gotchas\n\n- **Manifest before decorators**: every decorated model imports\n `../__smrt-register__.js`, and `package.json#sideEffects` lists\n `./src/__smrt-register__.ts`. Without both, the library build dropped or\n reordered the self-registration, so dist classes registered with only their\n decorated fields and could adopt smrt-ledgers' same-named `Account` (#3098).\n- **`Account` is not unique**: smrt-ledgers also declares `Account` (on\n `ledger_accounts`). Resolve by constructor or qualified name; the\n coexistence tests (`ledger-account-identity-*`, and the PostgreSQL lane\n `pnpm test:postgres`) load both packages in both orders.\n- **STI `_meta_type`**: qualified format `@happyvertical/smrt-messages:Email`\n- **JSON fields**: all address/metadata fields are JSON strings with `getX()`/`setX()` helpers\n- **RFC 822 threading**: Email uses `inReplyTo`/`messageId`/`references` — manual management required\n- **Attachment.messageId**: use this field (not `emailId`); old getter/setter mapped via deprecation wrapper\n- **Optional tenancy**: `@TenantScoped({ mode: 'optional' })` on Message, Account, Attachment\n- **Required tenancy**: endpoints and persona routes are always tenant scoped\n- **Secrets are write-only**: never serialize credentials or endpoint.address to an app\n"
15162
+ "agentDoc": "# @happyvertical/smrt-messages\n\nMulti-channel messaging with STI hierarchies for both messages and accounts. Credential encryption via smrt-secrets.\n\n## STI Hierarchies\n\n**Messages** (share `messages` table):\n- `Message` (base): accountId, personaId, endpointId, correlationId, threadId, subject, body, fromAddress, toAddresses (JSON), sendStatus (draft/sending/sent/failed), retryCount\n- `Email`: messageId (RFC 822), inReplyTo, ccAddresses, bccAddresses, htmlBody, textBody, folderId, labels, headers\n- `Tweet`: tweetId, retweetCount, likeCount, mediaUrls, hashtags, mentions\n- `SlackMessage`: channelId, slackTs, slackThreadTs, reactions, blocks\n\n**Accounts** (share `accounts` table):\n- `Account` (base): channelType, providerType, public configuration,\n credentialSecretId, isActive, lastSyncAt, deprecated sensitive settings\n- `EmailAccount`, `TwitterAccount`, `SlackAccount`, `ZulipAccount`,\n `TelegramAccount`: provider-specific identity/sender behavior\n\n## Persona messaging\n\n- `MessagingEndpoint`: tenant-owned, write-only destination JSON with masked\n display helper.\n- `PersonaMessageRoute`: persona → account + endpoint, selected by purpose and\n descending priority.\n- `PersonaMessagingService`: persists and sends through an enabled route.\n- Provider registry: public setup descriptors + optional sender factory. Keep\n channel (`sms`) separate from provider (`twilio`).\n- Permissions: `messages.send`, `messages.manage-routes`,\n `messages.manage-credentials`.\n- `MessagingSettingsService` is the permissioned server boundary;\n its host-supplied `resolvePersonaTenantId` proves cross-package ownership.\n `MessagingSettingsPanel` is the callback-driven Svelte surface.\n- Account, endpoint, and route generated surfaces are read-only. Do not add\n generated mutations around the settings service or make secret fields writable.\n\n## In-app notifications (`UserNotification`)\n\nThe in-app channel: one stored row per person, with read state.\n`user_notifications`: `tenantId` (required), `recipientUserId`\n(`@crossPackageRef` smrt-users:User, uuid), `kind`, `title`, `body`, `href`,\n`severity` (info/success/warning/error), `sourceRef`, `occurredAt`, `readAt`,\n`dismissedAt`. Conflict identity `(tenant_id, recipient_user_id, source_ref)`.\n\n- `UserNotificationService` is the only access path: `notify()` /\n `notifyMany()` (dedupe on `sourceRef`, existing row returned untouched;\n a unique ref is generated when omitted), `listForUser()` / `countUnread()`\n across explicit `tenantIds` (each read under `withTenant`), `markRead()`,\n `dismiss()` (also marks read), `markAllRead()`. Every write filters on the\n recipient, so another user's id matches nothing.\n- Generated REST/MCP/CLI are off (`api`/`mcp`/`cli: false`): a tenant-scoped\n list would expose every member's rows. Hosts expose their own routes that\n pass the signed-in user's id, never a request-chosen one.\n- Hosts decide who is told (e.g. `SocialPost.createdByUserId` for a failed\n post). New table: consumers run `db:migrate`.\n\n## Credential Security\n\nAccount credentials stored via tenant-bound `credentialSecretId` →\nsmrt-secrets. Use `setCredentials()`/`getCredentials()` — never store passwords\ndirectly. Credential descriptor schemas contain field definitions, never values.\n\n## Send Lifecycle\n\n`message.send()`: resolves account → creates provider sender → updates sendStatus. Retry support with `maxRetries` budget.\n\n## Provider entry points (bundle boundary, #1979)\n\nThe package root is provider-neutral: models, collections, services,\npermissions, and the provider registry carry **zero provider-SDK weight**.\nProvider SDK wrappers (`@happyvertical/email`, `@happyvertical/messages`) are\nreachable only through explicit entry points, imported once at server startup:\n\n```ts\nimport '@happyvertical/smrt-messages/providers/email'; // smtp/imap/pop3/gmail\nimport '@happyvertical/smrt-messages/providers/slack';\nimport '@happyvertical/smrt-messages/providers/twitter';\nimport '@happyvertical/smrt-messages/providers/all'; // all of the above\n```\n\n- Without the matching entry, `EmailAccount.createClient()` (send + mailbox\n sync) throws an actionable error and `SlackSender`/`TweetSender` return a\n failure result naming the entry to import. Telegram and Zulip are\n fetch-based and always work from the root.\n- Builtin registry definitions stay metadata-only (setup fields for UIs); the\n entries upgrade them with `createEmailClient`/`createMessageClient`/\n `createSender` hooks. Never import a provider SDK from any module reachable\n from `src/index.ts` — the consumer bundle gate (`packages/bundle-gate`)\n fails CI when one becomes reachable.\n- `Attachment.readContent()` loads `@happyvertical/files` through core's\n `importOptionalDependency` boundary and keeps its null-on-failure contract\n in builds where the SDK is absent.\n\n## Gotchas\n\n- **Manifest before decorators**: every decorated model imports\n `../__smrt-register__.js`, and `package.json#sideEffects` lists\n `./src/__smrt-register__.ts`. Without both, the library build dropped or\n reordered the self-registration, so dist classes registered with only their\n decorated fields and could adopt smrt-ledgers' same-named `Account` (#3098).\n- **`Account` is not unique**: smrt-ledgers also declares `Account` (on\n `ledger_accounts`). Resolve by constructor or qualified name; the\n coexistence tests (`ledger-account-identity-*`, and the PostgreSQL lane\n `pnpm test:postgres`) load both packages in both orders.\n- **STI `_meta_type`**: qualified format `@happyvertical/smrt-messages:Email`\n- **JSON fields**: all address/metadata fields are JSON strings with `getX()`/`setX()` helpers\n- **RFC 822 threading**: Email uses `inReplyTo`/`messageId`/`references` — manual management required\n- **Attachment.messageId**: use this field (not `emailId`); old getter/setter mapped via deprecation wrapper\n- **Optional tenancy**: `@TenantScoped({ mode: 'optional' })` on Message, Account, Attachment\n- **Required tenancy**: endpoints and persona routes are always tenant scoped\n- **Secrets are write-only**: never serialize credentials or endpoint.address to an app\n\n## Intake byte snapshots\n\n`EmailAccount.readIntakeMessage(providerLookup, maxBytes, imapIdentity?)` is the authorized host seam for\nopt-in ingestion. It copies full-message attachment bytes from the registered\nemail provider and fails when only attachment metadata is available; it never\nreads provider-supplied paths or advances sync checkpoints. Keep this path\nprovider-neutral and exclude arbitrary headers/credentials from its snapshot.\nIMAP lookup must include folder/UIDVALIDITY/UID verification; sender Message-ID\nis a lookup hint, never the durable delivery identity.\n"
15142
15163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-messages",
3
- "version": "0.55.4",
3
+ "version": "0.55.7",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Unified multi-channel messaging with STI-based hierarchies",
6
6
  "type": "module",
@@ -58,18 +58,18 @@
58
58
  "./dist/svelte/index.js"
59
59
  ],
60
60
  "dependencies": {
61
- "@happyvertical/email": "^0.102.3",
62
- "@happyvertical/files": "^0.102.3",
63
- "@happyvertical/logger": "^0.102.3",
64
- "@happyvertical/messages": "^0.102.3",
65
- "@happyvertical/smrt-core": "0.55.4",
66
- "@happyvertical/smrt-types": "0.55.4",
67
- "@happyvertical/smrt-secrets": "0.55.4",
68
- "@happyvertical/smrt-tenancy": "0.55.4",
69
- "@happyvertical/smrt-ui": "0.55.4",
70
- "@happyvertical/smrt-users": "0.55.4",
71
- "@happyvertical/sql": "^0.102.3",
72
- "@happyvertical/utils": "^0.102.3"
61
+ "@happyvertical/email": "^0.102.5",
62
+ "@happyvertical/files": "^0.102.5",
63
+ "@happyvertical/logger": "^0.102.5",
64
+ "@happyvertical/messages": "^0.102.5",
65
+ "@happyvertical/smrt-core": "0.55.7",
66
+ "@happyvertical/smrt-types": "0.55.7",
67
+ "@happyvertical/smrt-secrets": "0.55.7",
68
+ "@happyvertical/smrt-tenancy": "0.55.7",
69
+ "@happyvertical/smrt-ui": "0.55.7",
70
+ "@happyvertical/smrt-users": "0.55.7",
71
+ "@happyvertical/sql": "^0.102.5",
72
+ "@happyvertical/utils": "^0.102.5"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "@happyvertical/encryption": "*",
@@ -84,8 +84,8 @@
84
84
  }
85
85
  },
86
86
  "devDependencies": {
87
- "@happyvertical/smrt-ledgers": "0.55.4",
88
- "@happyvertical/smrt-vitest": "0.55.4",
87
+ "@happyvertical/smrt-ledgers": "0.55.7",
88
+ "@happyvertical/smrt-vitest": "0.55.7",
89
89
  "@sveltejs/package": "^2.5.8",
90
90
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
91
91
  "@types/node": "24.13.6",