@amalgm/channels 0.1.0 → 0.1.2

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 (127) hide show
  1. package/PURPOSE.md +50 -13
  2. package/README.md +64 -6
  3. package/dist/host/auth.js +7 -3
  4. package/dist/host/auth.js.map +1 -1
  5. package/dist/host/config.d.ts +4 -1
  6. package/dist/host/config.js +4 -1
  7. package/dist/host/config.js.map +1 -1
  8. package/dist/host/inbox-repository.d.ts +10 -0
  9. package/dist/host/inbox-repository.js +57 -0
  10. package/dist/host/inbox-repository.js.map +1 -0
  11. package/dist/host/index.d.ts +4 -0
  12. package/dist/host/index.js +4 -0
  13. package/dist/host/index.js.map +1 -1
  14. package/dist/host/ingress-repository.d.ts +10 -0
  15. package/dist/host/ingress-repository.js +61 -0
  16. package/dist/host/ingress-repository.js.map +1 -0
  17. package/dist/host/main.js +38 -3
  18. package/dist/host/main.js.map +1 -1
  19. package/dist/host/recipient.js.map +1 -1
  20. package/dist/host/repository.d.ts +4 -3
  21. package/dist/host/repository.js +23 -3
  22. package/dist/host/repository.js.map +1 -1
  23. package/dist/host/resend-webhook.d.ts +14 -0
  24. package/dist/host/resend-webhook.js +172 -0
  25. package/dist/host/resend-webhook.js.map +1 -0
  26. package/dist/host/resend.js +4 -0
  27. package/dist/host/resend.js.map +1 -1
  28. package/dist/host/server.d.ts +3 -1
  29. package/dist/host/server.js +87 -19
  30. package/dist/host/server.js.map +1 -1
  31. package/dist/host/wakeups.d.ts +22 -0
  32. package/dist/host/wakeups.js +30 -0
  33. package/dist/host/wakeups.js.map +1 -0
  34. package/dist/src/api-error.d.ts +2 -0
  35. package/dist/src/api-error.js +13 -0
  36. package/dist/src/api-error.js.map +1 -0
  37. package/dist/src/cli/arguments.d.ts +20 -0
  38. package/dist/src/cli/arguments.js +46 -0
  39. package/dist/src/cli/arguments.js.map +1 -0
  40. package/dist/src/cli/input.d.ts +6 -0
  41. package/dist/src/cli/input.js +38 -0
  42. package/dist/src/cli/input.js.map +1 -0
  43. package/dist/src/cli/main.d.ts +7 -0
  44. package/dist/src/cli/main.js +27 -0
  45. package/dist/src/cli/main.js.map +1 -0
  46. package/dist/src/cli/run.d.ts +13 -0
  47. package/dist/src/cli/run.js +47 -0
  48. package/dist/src/cli/run.js.map +1 -0
  49. package/dist/src/cli/shell-runtime.d.ts +9 -0
  50. package/dist/src/cli/shell-runtime.js +106 -0
  51. package/dist/src/cli/shell-runtime.js.map +1 -0
  52. package/dist/src/cli-main.d.ts +2 -0
  53. package/dist/src/cli-main.js +4 -0
  54. package/dist/src/cli-main.js.map +1 -0
  55. package/dist/src/cli.d.ts +3 -0
  56. package/dist/src/cli.js +4 -0
  57. package/dist/src/cli.js.map +1 -0
  58. package/dist/src/client.js +5 -4
  59. package/dist/src/client.js.map +1 -1
  60. package/dist/src/command-surface.d.ts +23 -0
  61. package/dist/src/command-surface.js +53 -0
  62. package/dist/src/command-surface.js.map +1 -0
  63. package/dist/src/contract.d.ts +33 -7
  64. package/dist/src/email/blocks.d.ts +7 -0
  65. package/dist/src/email/blocks.js +43 -0
  66. package/dist/src/email/blocks.js.map +1 -0
  67. package/dist/src/email/escape.d.ts +3 -0
  68. package/dist/src/email/escape.js +19 -0
  69. package/dist/src/email/escape.js.map +1 -0
  70. package/dist/src/email/inbound.d.ts +83 -0
  71. package/dist/src/email/inbound.js +162 -0
  72. package/dist/src/email/inbound.js.map +1 -0
  73. package/dist/src/email/inline.d.ts +1 -0
  74. package/dist/src/email/inline.js +31 -0
  75. package/dist/src/email/inline.js.map +1 -0
  76. package/dist/src/email/markdown.d.ts +1 -0
  77. package/dist/src/email/markdown.js +117 -0
  78. package/dist/src/email/markdown.js.map +1 -0
  79. package/dist/src/email/notification.d.ts +2 -0
  80. package/dist/src/email/notification.js +18 -0
  81. package/dist/src/email/notification.js.map +1 -0
  82. package/dist/src/email/styles.d.ts +33 -0
  83. package/dist/src/email/styles.js +34 -0
  84. package/dist/src/email/styles.js.map +1 -0
  85. package/dist/src/email/system.d.ts +13 -0
  86. package/dist/src/email/system.js +31 -0
  87. package/dist/src/email/system.js.map +1 -0
  88. package/dist/src/email/thread-token.d.ts +8 -0
  89. package/dist/src/email/thread-token.js +70 -0
  90. package/dist/src/email/thread-token.js.map +1 -0
  91. package/dist/src/errors.d.ts +3 -2
  92. package/dist/src/errors.js +3 -1
  93. package/dist/src/errors.js.map +1 -1
  94. package/dist/src/http.d.ts +2 -0
  95. package/dist/src/http.js +84 -3
  96. package/dist/src/http.js.map +1 -1
  97. package/dist/src/inbox/client.d.ts +7 -0
  98. package/dist/src/inbox/client.js +79 -0
  99. package/dist/src/inbox/client.js.map +1 -0
  100. package/dist/src/inbox/contract.d.ts +37 -0
  101. package/dist/src/inbox/contract.js +2 -0
  102. package/dist/src/inbox/contract.js.map +1 -0
  103. package/dist/src/inbox/events.d.ts +7 -0
  104. package/dist/src/inbox/events.js +46 -0
  105. package/dist/src/inbox/events.js.map +1 -0
  106. package/dist/src/inbox/runner.d.ts +14 -0
  107. package/dist/src/inbox/runner.js +93 -0
  108. package/dist/src/inbox/runner.js.map +1 -0
  109. package/dist/src/inbox/service.d.ts +9 -0
  110. package/dist/src/inbox/service.js +56 -0
  111. package/dist/src/inbox/service.js.map +1 -0
  112. package/dist/src/index.d.ts +11 -0
  113. package/dist/src/index.js +11 -0
  114. package/dist/src/index.js.map +1 -1
  115. package/dist/src/mcp.js +20 -52
  116. package/dist/src/mcp.js.map +1 -1
  117. package/dist/src/service.js +7 -3
  118. package/dist/src/service.js.map +1 -1
  119. package/dist/src/validation.d.ts +2 -2
  120. package/dist/src/validation.js +2 -2
  121. package/dist/src/validation.js.map +1 -1
  122. package/dist/src/worker.d.ts +4 -1
  123. package/dist/src/worker.js +27 -5
  124. package/dist/src/worker.js.map +1 -1
  125. package/package.json +38 -12
  126. package/skills/channels/SKILL.md +14 -14
  127. package/supabase/migrations/20260903000000_bidirectional_email.sql +630 -0
@@ -0,0 +1,83 @@
1
+ import type { EmailThreadAddressing } from './thread-token.js';
2
+ export interface ReceivedEmail {
3
+ readonly providerEventId: string;
4
+ readonly providerEmailId: string;
5
+ readonly providerMessageId: string;
6
+ readonly from: string;
7
+ readonly to: readonly string[];
8
+ readonly subject: string;
9
+ readonly text?: string | null;
10
+ readonly html?: string | null;
11
+ readonly headers: Readonly<Record<string, string>>;
12
+ readonly receivedAt: string;
13
+ }
14
+ export interface NormalizedReceivedEmail {
15
+ readonly providerEventId: string;
16
+ readonly providerEmailId: string;
17
+ readonly providerMessageId: string;
18
+ readonly senderEmail: string;
19
+ readonly subject: string;
20
+ readonly message: string;
21
+ readonly threadHintId?: string;
22
+ readonly replyMessageIds: readonly string[];
23
+ readonly receivedAt: string;
24
+ }
25
+ export type InboundAdmission = Readonly<{
26
+ kind: 'accepted';
27
+ userId: string;
28
+ messageId: string;
29
+ threadId: string;
30
+ sessionId: string;
31
+ targetComputerId: string;
32
+ targetComputerName: string;
33
+ targetOnline: boolean;
34
+ senderEmail: string;
35
+ subject: string;
36
+ providerMessageId: string;
37
+ references: readonly string[];
38
+ }> | Readonly<{
39
+ kind: 'unknown_sender';
40
+ recordId: string;
41
+ senderEmail: string;
42
+ subject: string;
43
+ providerMessageId: string;
44
+ references: readonly string[];
45
+ }> | Readonly<{
46
+ kind: 'no_computer';
47
+ recordId: string;
48
+ senderEmail: string;
49
+ subject: string;
50
+ providerMessageId: string;
51
+ references: readonly string[];
52
+ }> | Readonly<{
53
+ kind: 'duplicate';
54
+ recordId: string;
55
+ }> | Readonly<{
56
+ kind: 'ignored';
57
+ reason: string;
58
+ }>;
59
+ export interface EmailIngressRepository {
60
+ admit(input: NormalizedReceivedEmail): Promise<InboundAdmission>;
61
+ recordNotice(recordId: string, providerId: string, now: Date): Promise<boolean>;
62
+ applyProviderEvent(input: ProviderEmailEvent): Promise<boolean>;
63
+ }
64
+ export interface ProviderEmailEvent {
65
+ readonly providerEventId: string;
66
+ readonly providerEmailId: string;
67
+ readonly type: 'sent' | 'delivered' | 'failed';
68
+ readonly providerMessageId?: string;
69
+ readonly error?: string;
70
+ readonly occurredAt: string;
71
+ }
72
+ export declare class EmailIngress {
73
+ private readonly repository;
74
+ private readonly addressing;
75
+ constructor(repository: EmailIngressRepository, addressing: EmailThreadAddressing);
76
+ receive(input: ReceivedEmail): Promise<InboundAdmission>;
77
+ providerEvent(input: ProviderEmailEvent): Promise<boolean>;
78
+ repositoryNotice(recordId: string, providerId: string, now: Date): Promise<boolean>;
79
+ }
80
+ /** Only the receiver's aligned DMARC verdict can authorize a visible From address. */
81
+ export declare function authenticatedSender(headers: Readonly<Record<string, string>>, senderEmail: string): boolean;
82
+ /** Reduce provider plaintext to the newly authored reply, never the quoted transcript. */
83
+ export declare function visibleReply(value: string): string;
@@ -0,0 +1,162 @@
1
+ import { ChannelsError } from '../errors.js';
2
+ export class EmailIngress {
3
+ repository;
4
+ addressing;
5
+ constructor(repository, addressing) {
6
+ this.repository = repository;
7
+ this.addressing = addressing;
8
+ }
9
+ async receive(input) {
10
+ const headers = lowerHeaders(input.headers);
11
+ const ignored = ignoredReason(headers);
12
+ if (ignored)
13
+ return Object.freeze({ kind: 'ignored', reason: ignored });
14
+ const senderEmail = sender(input.from);
15
+ if (!authenticatedSender(headers, senderEmail)) {
16
+ return Object.freeze({ kind: 'ignored', reason: 'unauthenticated_sender' });
17
+ }
18
+ const message = emailBody(input.text, input.html);
19
+ const threadHintId = await this.addressing.threadHint(input.to);
20
+ return this.repository.admit(Object.freeze({
21
+ providerEventId: bounded(input.providerEventId, 'providerEventId', 1, 200),
22
+ providerEmailId: bounded(input.providerEmailId, 'providerEmailId', 1, 200),
23
+ providerMessageId: messageId(input.providerMessageId, 'providerMessageId'),
24
+ senderEmail,
25
+ subject: subject(input.subject),
26
+ message,
27
+ ...(threadHintId ? { threadHintId } : {}),
28
+ replyMessageIds: Object.freeze(replyIds(headers)),
29
+ receivedAt: timestamp(input.receivedAt, 'receivedAt'),
30
+ }));
31
+ }
32
+ providerEvent(input) {
33
+ return this.repository.applyProviderEvent(Object.freeze({
34
+ providerEventId: bounded(input.providerEventId, 'providerEventId', 1, 200),
35
+ providerEmailId: bounded(input.providerEmailId, 'providerEmailId', 1, 200),
36
+ type: input.type,
37
+ ...(input.providerMessageId
38
+ ? { providerMessageId: messageId(input.providerMessageId, 'providerMessageId') }
39
+ : {}),
40
+ ...(input.error ? { error: bounded(input.error, 'error', 1, 500) } : {}),
41
+ occurredAt: timestamp(input.occurredAt, 'occurredAt'),
42
+ }));
43
+ }
44
+ repositoryNotice(recordId, providerId, now) {
45
+ return this.repository.recordNotice(recordId, providerId, now);
46
+ }
47
+ }
48
+ function lowerHeaders(value) {
49
+ return Object.fromEntries(Object.entries(value).map(([name, content]) => [name.toLowerCase(), String(content)]));
50
+ }
51
+ function ignoredReason(headers) {
52
+ const autoSubmitted = headers['auto-submitted']?.toLowerCase();
53
+ if (autoSubmitted && autoSubmitted !== 'no')
54
+ return 'auto_submitted';
55
+ if (/bulk|junk|list/i.test(headers['precedence'] ?? ''))
56
+ return 'bulk_message';
57
+ if (headers['x-autoreply'] || headers['x-autorespond'])
58
+ return 'auto_reply';
59
+ return null;
60
+ }
61
+ /** Only the receiver's aligned DMARC verdict can authorize a visible From address. */
62
+ export function authenticatedSender(headers, senderEmail) {
63
+ const fromDomain = senderEmail.slice(senderEmail.lastIndexOf('@') + 1).toLowerCase();
64
+ const results = headers['authentication-results'] ?? '';
65
+ const verdicts = [...results.matchAll(/(?:^|;)\s*dmarc\s*=\s*([a-z_]+)([^;]*)/gi)]
66
+ .map((match) => Object.freeze({
67
+ verdict: match[1]?.toLowerCase() ?? '',
68
+ domain: match[2]?.match(/\bheader\.from\s*=\s*([^\s;()]+)/i)?.[1]
69
+ ?.replace(/^['"]|['"]$/g, '').toLowerCase() ?? '',
70
+ }))
71
+ .filter(({ domain }) => domain === fromDomain);
72
+ return verdicts.length > 0
73
+ && verdicts.every(({ verdict }) => verdict === 'pass');
74
+ }
75
+ function replyIds(headers) {
76
+ const values = `${headers['in-reply-to'] ?? ''} ${headers.references ?? ''}`;
77
+ return [...new Set(values.match(/<[^<>\r\n]{1,500}>/g) ?? [])].slice(-50);
78
+ }
79
+ function emailBody(text, html) {
80
+ const plain = typeof text === 'string' ? text.replaceAll('\r\n', '\n').trim() : '';
81
+ const fallback = visibleReply(plain || htmlText(html ?? ''));
82
+ return bounded(fallback, 'message', 1, 32_000);
83
+ }
84
+ function htmlText(value) {
85
+ return value
86
+ .replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi, ' ')
87
+ .replace(/<blockquote[^>]*>[\s\S]*?<\/blockquote>/gi, ' ')
88
+ .replace(/<div[^>]*class=["'][^"']*gmail_quote[^"']*["'][^>]*>[\s\S]*?<\/div>/gi, ' ')
89
+ .replace(/<br\s*\/?>/gi, '\n')
90
+ .replace(/<\/p\s*>/gi, '\n\n')
91
+ .replace(/<[^>]+>/g, ' ')
92
+ .replace(/&nbsp;/gi, ' ')
93
+ .replace(/&amp;/gi, '&')
94
+ .replace(/&lt;/gi, '<')
95
+ .replace(/&gt;/gi, '>')
96
+ .replace(/&quot;/gi, '"')
97
+ .replace(/&#39;/gi, "'")
98
+ .replace(/[ \t]+/g, ' ')
99
+ .replace(/\n{3,}/g, '\n\n')
100
+ .trim();
101
+ }
102
+ /** Reduce provider plaintext to the newly authored reply, never the quoted transcript. */
103
+ export function visibleReply(value) {
104
+ const lines = value.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
105
+ let boundary = lines.length;
106
+ for (let index = 0; index < lines.length; index += 1) {
107
+ const line = lines[index]?.trim() ?? '';
108
+ const gmailBoundary = [1, 2, 3, 4].some((count) => /^On\s.{1,500}\bwrote:\s*$/i.test(lines.slice(index, Math.min(lines.length, index + count)).join(' ').replace(/\s+/g, ' ').trim()));
109
+ if (gmailBoundary
110
+ || /^-{5,}\s*Original Message\s*-{5,}$/i.test(line)
111
+ || (/^_{5,}$/.test(line) && outlookHeaders(lines, index + 1))
112
+ || (index > 0 && /^From:\s+\S/i.test(line) && outlookHeaders(lines, index))) {
113
+ boundary = index;
114
+ break;
115
+ }
116
+ if (index > 0 && /^>/.test(line) && lines.slice(index).filter((item) => item.trim()).every((item) => /^>/.test(item.trim()))) {
117
+ boundary = index;
118
+ break;
119
+ }
120
+ }
121
+ const visible = lines.slice(0, boundary).join('\n').trim();
122
+ return visible || value.trim();
123
+ }
124
+ function outlookHeaders(lines, start) {
125
+ const headers = lines.slice(start, Math.min(lines.length, start + 8))
126
+ .map((line) => line.trim().toLowerCase());
127
+ return headers.some((line) => line.startsWith('from:'))
128
+ && headers.some((line) => line.startsWith('to:') || line.startsWith('sent:') || line.startsWith('date:'))
129
+ && headers.some((line) => line.startsWith('subject:'));
130
+ }
131
+ function sender(value) {
132
+ const match = String(value).trim().toLowerCase().match(/<?([^<>\s]+@[^<>\s]+)>?$/);
133
+ if (!match?.[1] || match[1].length > 320)
134
+ throw new ChannelsError('validation', 'Sender email is invalid');
135
+ return match[1];
136
+ }
137
+ function subject(value) {
138
+ const clean = String(value ?? '').replace(/[\r\n]+/g, ' ').trim();
139
+ return (clean || '(no subject)').slice(0, 500);
140
+ }
141
+ function messageId(value, name) {
142
+ const clean = bounded(value, name, 3, 500);
143
+ if (!/^<[^<>\r\n]+>$/.test(clean))
144
+ throw new ChannelsError('validation', `${name} is invalid`);
145
+ return clean;
146
+ }
147
+ function timestamp(value, name) {
148
+ const date = new Date(value);
149
+ if (!Number.isFinite(date.getTime()))
150
+ throw new ChannelsError('validation', `${name} is invalid`);
151
+ return date.toISOString();
152
+ }
153
+ function bounded(value, name, minimum, maximum) {
154
+ if (typeof value !== 'string')
155
+ throw new ChannelsError('validation', `${name} must be a string`);
156
+ const clean = value.trim();
157
+ if (clean.length < minimum || clean.length > maximum) {
158
+ throw new ChannelsError('validation', `${name} must be ${minimum}-${maximum} characters`);
159
+ }
160
+ return clean;
161
+ }
162
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/email/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function renderInline(source: string): string;
@@ -0,0 +1,31 @@
1
+ import { escapeAttribute, escapeText, safeLink } from './escape.js';
2
+ import { EMAIL } from './styles.js';
3
+ const marker = (index) => `\u0000${index}\u0000`;
4
+ export function renderInline(source) {
5
+ const tokens = [];
6
+ let text = source;
7
+ text = text.replace(/`([^`\n]+)`/g, (_match, code) => keep(tokens, inlineCode(code)));
8
+ text = text.replace(/\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g, (_match, label, href) => keep(tokens, link(label, href)));
9
+ text = escapeText(text)
10
+ .replace(/\*\*\*([^*\n]+)\*\*\*/g, '<strong style="color:#ffffff;font-weight:600"><em>$1</em></strong>')
11
+ .replace(/___([^_\n]+)___/g, '<strong style="color:#ffffff;font-weight:600"><em>$1</em></strong>')
12
+ .replace(/\*\*([^*\n]+)\*\*/g, '<strong style="color:#ffffff;font-weight:600">$1</strong>')
13
+ .replace(/__([^_\n]+)__/g, '<strong style="color:#ffffff;font-weight:600">$1</strong>')
14
+ .replace(/(^|\s)\*([^*\n]+)\*(?=\s|[.,!?;:]|$)/g, '$1<em>$2</em>')
15
+ .replace(/(^|\s)_([^_\n]+)_(?=\s|[.,!?;:]|$)/g, '$1<em>$2</em>');
16
+ return text.replace(/\u0000(\d+)\u0000/g, (_match, index) => tokens[Number(index)] ?? '');
17
+ }
18
+ function keep(tokens, html) {
19
+ tokens.push(html);
20
+ return marker(tokens.length - 1);
21
+ }
22
+ function inlineCode(code) {
23
+ return `<code style="background:${EMAIL.color.inset};border:1px solid ${EMAIL.color.border};padding:1.875px 7.5px;border-radius:5.625px;font-size:12px;line-height:1.2;font-family:${EMAIL.font.mono};color:#f5f5f4">${escapeText(code)}</code>`;
24
+ }
25
+ function link(label, rawHref) {
26
+ const href = safeLink(rawHref);
27
+ if (!href)
28
+ return escapeText(label);
29
+ return `<a href="${escapeAttribute(href)}" target="_blank" rel="noreferrer" style="color:${EMAIL.color.link};text-decoration:none">${escapeText(label)}</a>`;
30
+ }
31
+ //# sourceMappingURL=inline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.js","sourceRoot":"","sources":["../../../src/email/inline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,SAAS,KAAK,QAAQ,CAAC;AAEjE,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,IAAI,GAAG,MAAM,CAAC;IAElB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9F,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,6CAA6C,EAAE,CACjE,MAAM,EACN,KAAa,EACb,IAAY,EACZ,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAEtC,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;SACpB,OAAO,CAAC,wBAAwB,EAAE,oEAAoE,CAAC;SACvG,OAAO,CAAC,kBAAkB,EAAE,oEAAoE,CAAC;SACjG,OAAO,CAAC,oBAAoB,EAAE,2DAA2D,CAAC;SAC1F,OAAO,CAAC,gBAAgB,EAAE,2DAA2D,CAAC;SACtF,OAAO,CAAC,uCAAuC,EAAE,eAAe,CAAC;SACjE,OAAO,CAAC,qCAAqC,EAAE,eAAe,CAAC,CAAC;IAEnE,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,SAAS,IAAI,CAAC,MAAgB,EAAE,IAAY;IAC1C,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,2BAA2B,KAAK,CAAC,KAAK,CAAC,KAAK,qBAAqB,KAAK,CAAC,KAAK,CAAC,MAAM,2FAA2F,KAAK,CAAC,IAAI,CAAC,IAAI,mBAAmB,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;AACnP,CAAC;AAED,SAAS,IAAI,CAAC,KAAa,EAAE,OAAe;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,YAAY,eAAe,CAAC,IAAI,CAAC,mDAAmD,KAAK,CAAC,KAAK,CAAC,IAAI,0BAA0B,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;AAC/J,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function renderEmailMarkdown(markdown: string): string;
@@ -0,0 +1,117 @@
1
+ import { codeBlock, divider, heading, list, paragraph, quote, table } from './blocks.js';
2
+ import { EMAIL } from './styles.js';
3
+ export function renderEmailMarkdown(markdown) {
4
+ const lines = markdown.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
5
+ const blocks = [];
6
+ for (let index = 0; index < lines.length;) {
7
+ const line = lines[index] ?? '';
8
+ const trimmed = line.trim();
9
+ if (!trimmed) {
10
+ index += 1;
11
+ continue;
12
+ }
13
+ const fence = trimmed.match(/^```([\w+-]*)\s*$/);
14
+ if (fence) {
15
+ const code = [];
16
+ index += 1;
17
+ while (index < lines.length && !/^```\s*$/.test((lines[index] ?? '').trim())) {
18
+ code.push(lines[index] ?? '');
19
+ index += 1;
20
+ }
21
+ if (index < lines.length)
22
+ index += 1;
23
+ blocks.push(codeBlock(fence[1] ?? '', code.join('\n')));
24
+ continue;
25
+ }
26
+ const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
27
+ if (headingMatch) {
28
+ blocks.push(heading(headingMatch[1]?.length ?? 1, headingMatch[2] ?? ''));
29
+ index += 1;
30
+ continue;
31
+ }
32
+ if (/^(?:---+|___+|\*\*\*+)\s*$/.test(trimmed)) {
33
+ blocks.push(divider());
34
+ index += 1;
35
+ continue;
36
+ }
37
+ const tableRows = readTable(lines, index);
38
+ if (tableRows) {
39
+ blocks.push(table(tableRows.rows));
40
+ index = tableRows.next;
41
+ continue;
42
+ }
43
+ if (/^>\s?/.test(trimmed)) {
44
+ const result = collect(lines, index, (value) => {
45
+ const match = value.trim().match(/^>\s?(.*)$/);
46
+ return match?.[1] ?? null;
47
+ });
48
+ blocks.push(quote(result.values));
49
+ index = result.next;
50
+ continue;
51
+ }
52
+ const unordered = trimmed.match(/^[-*]\s+(.+)$/);
53
+ const ordered = trimmed.match(/^\d+\.\s+(.+)$/);
54
+ if (unordered || ordered) {
55
+ const isOrdered = Boolean(ordered);
56
+ const result = collect(lines, index, (value) => {
57
+ const pattern = isOrdered ? /^\d+\.\s+(.+)$/ : /^[-*]\s+(.+)$/;
58
+ return value.trim().match(pattern)?.[1] ?? null;
59
+ });
60
+ blocks.push(list(isOrdered, result.values));
61
+ index = result.next;
62
+ continue;
63
+ }
64
+ const prose = [];
65
+ while (index < lines.length && !startsBlock(lines, index)) {
66
+ prose.push((lines[index] ?? '').trim());
67
+ index += 1;
68
+ }
69
+ blocks.push(paragraph(prose));
70
+ }
71
+ return blocks.map((block, index) => index === 0
72
+ ? block
73
+ : `<div style="margin:${EMAIL.blockGap}px 0 0">${block}</div>`).join('\n');
74
+ }
75
+ function startsBlock(lines, index) {
76
+ const value = (lines[index] ?? '').trim();
77
+ if (!value)
78
+ return true;
79
+ if (/^(?:```|#{1,6}\s|>\s?|[-*]\s+|\d+\.\s+|---+$|___+$|\*\*\*+$)/.test(value))
80
+ return true;
81
+ return readTable(lines, index) !== null;
82
+ }
83
+ function collect(lines, start, parse) {
84
+ const values = [];
85
+ let next = start;
86
+ while (next < lines.length) {
87
+ const value = parse(lines[next] ?? '');
88
+ if (value === null)
89
+ break;
90
+ values.push(value);
91
+ next += 1;
92
+ }
93
+ return { values, next };
94
+ }
95
+ function readTable(lines, start) {
96
+ const header = tableRow(lines[start] ?? '');
97
+ const separator = tableRow(lines[start + 1] ?? '');
98
+ if (!header || !separator || !separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
99
+ return null;
100
+ const rows = [header];
101
+ let next = start + 2;
102
+ while (next < lines.length) {
103
+ const row = tableRow(lines[next] ?? '');
104
+ if (!row)
105
+ break;
106
+ rows.push(row);
107
+ next += 1;
108
+ }
109
+ return { rows, next };
110
+ }
111
+ function tableRow(value) {
112
+ const trimmed = value.trim();
113
+ if (!trimmed.startsWith('|') || !trimmed.endsWith('|'))
114
+ return null;
115
+ return trimmed.slice(1, -1).split('|').map((cell) => cell.trim());
116
+ }
117
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/email/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** The approved Engine notification presentation, now owned by Channels. */
2
+ export declare function renderNotificationEmail(message: string): string;
@@ -0,0 +1,18 @@
1
+ import { renderEmailMarkdown } from './markdown.js';
2
+ import { EMAIL } from './styles.js';
3
+ /** The approved Engine notification presentation, now owned by Channels. */
4
+ export function renderNotificationEmail(message) {
5
+ const body = renderEmailMarkdown(message);
6
+ return `<!DOCTYPE html>
7
+ <html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
8
+ <body style="margin:0;padding:0;background:${EMAIL.color.background};font-family:${EMAIL.font.text}">
9
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${EMAIL.color.background}"><tr><td align="center" style="padding:0">
10
+ <table role="presentation" width="520" cellpadding="0" cellspacing="0" style="max-width:520px;width:100%"><tr><td style="padding:40px 28px 48px">
11
+ <div style="font-size:${EMAIL.bodySize}px;color:${EMAIL.color.text}">${body}</div>
12
+
13
+ <p style="margin:48px 0 0;font-size:12px;color:${EMAIL.color.text}">amalgm.ai</p>
14
+ </td></tr></table>
15
+ </td></tr></table>
16
+ </body></html>`;
17
+ }
18
+ //# sourceMappingURL=notification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.js","sourceRoot":"","sources":["../../../src/email/notification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO;;6CAEoC,KAAK,CAAC,KAAK,CAAC,UAAU,gBAAgB,KAAK,CAAC,IAAI,CAAC,IAAI;4FACN,KAAK,CAAC,KAAK,CAAC,UAAU;;4BAEtF,KAAK,CAAC,QAAQ,YAAY,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI;;qDAE1B,KAAK,CAAC,KAAK,CAAC,IAAI;;;eAGtD,CAAC;AAChB,CAAC"}
@@ -0,0 +1,33 @@
1
+ export declare const EMAIL: Readonly<{
2
+ font: Readonly<{
3
+ text: "\"SF Pro Text\", \"SF Pro Display\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
4
+ mono: "'JetBrains Mono', 'SFMono-Regular', Menlo, monospace";
5
+ }>;
6
+ color: Readonly<{
7
+ background: "#000000";
8
+ text: "#ffffff";
9
+ secondary: "#d6d3d1";
10
+ muted: "#a8a29e";
11
+ border: "#1c1917";
12
+ grid: "#292524";
13
+ inset: "#0a0a0a";
14
+ link: "#60a5fa";
15
+ }>;
16
+ bodySize: 14;
17
+ blockGap: 15;
18
+ }>;
19
+ export declare const BLOCK: Readonly<{
20
+ headingSizes: readonly number[];
21
+ headingLineHeight: 1.35;
22
+ headingWeight: 650;
23
+ paragraphLineHeight: 1.75;
24
+ listPaddingLeft: 24;
25
+ listItemPaddingY: 3.75;
26
+ listItemLineHeight: 1.5;
27
+ quotePaddingLeft: 16;
28
+ codeRadius: 12;
29
+ codePadding: 15;
30
+ tableRadius: 12;
31
+ tableCellPaddingX: 11.25;
32
+ tableCellPaddingY: 5.625;
33
+ }>;
@@ -0,0 +1,34 @@
1
+ export const EMAIL = Object.freeze({
2
+ font: Object.freeze({
3
+ text: '"SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
4
+ mono: "'JetBrains Mono', 'SFMono-Regular', Menlo, monospace",
5
+ }),
6
+ color: Object.freeze({
7
+ background: '#000000',
8
+ text: '#ffffff',
9
+ secondary: '#d6d3d1',
10
+ muted: '#a8a29e',
11
+ border: '#1c1917',
12
+ grid: '#292524',
13
+ inset: '#0a0a0a',
14
+ link: '#60a5fa',
15
+ }),
16
+ bodySize: 14,
17
+ blockGap: 15,
18
+ });
19
+ export const BLOCK = Object.freeze({
20
+ headingSizes: Object.freeze([18, 16, 15, 14, 14, 14]),
21
+ headingLineHeight: 1.35,
22
+ headingWeight: 650,
23
+ paragraphLineHeight: 1.75,
24
+ listPaddingLeft: 24,
25
+ listItemPaddingY: 3.75,
26
+ listItemLineHeight: 1.5,
27
+ quotePaddingLeft: 16,
28
+ codeRadius: 12,
29
+ codePadding: 15,
30
+ tableRadius: 12,
31
+ tableCellPaddingX: 11.25,
32
+ tableCellPaddingY: 5.625,
33
+ });
34
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/email/styles.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,2FAA2F;QACjG,IAAI,EAAE,sDAAsD;KAC7D,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QACnB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;KAChB,CAAC;IACF,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;CACb,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACrD,iBAAiB,EAAE,IAAI;IACvB,aAAa,EAAE,GAAG;IAClB,mBAAmB,EAAE,IAAI;IACzB,eAAe,EAAE,EAAE;IACnB,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,GAAG;IACvB,gBAAgB,EAAE,EAAE;IACpB,UAAU,EAAE,EAAE;IACd,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,EAAE;IACf,iBAAiB,EAAE,KAAK;IACxB,iBAAiB,EAAE,KAAK;CACzB,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export type InboundSystemNotice = Readonly<{
2
+ kind: 'unknown_sender';
3
+ }> | Readonly<{
4
+ kind: 'no_computer';
5
+ }> | Readonly<{
6
+ kind: 'computer_offline';
7
+ computerName: string;
8
+ }>;
9
+ export declare function inboundSystemNotice(notice: InboundSystemNotice): Readonly<{
10
+ subjectPrefix: string;
11
+ text: string;
12
+ html: string;
13
+ }>;
@@ -0,0 +1,31 @@
1
+ import { renderNotificationEmail } from './notification.js';
2
+ export function inboundSystemNotice(notice) {
3
+ const text = noticeText(notice);
4
+ return Object.freeze({ subjectPrefix: 'Re:', text, html: renderNotificationEmail(text) });
5
+ }
6
+ function noticeText(notice) {
7
+ if (notice.kind === 'unknown_sender') {
8
+ return [
9
+ '## We couldn’t find your Amalgm account',
10
+ '',
11
+ 'This email address is not attached to an Amalgm account. Send your message again from the email address you use to sign in.',
12
+ '',
13
+ '[Open Amalgm](https://amalgm.ai)',
14
+ ].join('\n');
15
+ }
16
+ if (notice.kind === 'no_computer') {
17
+ return [
18
+ '## No Amalgm computer is available',
19
+ '',
20
+ 'Your account does not have a computer that can run this message yet. Open Amalgm on a computer, then send your message again.',
21
+ '',
22
+ '[Open Amalgm](https://amalgm.ai)',
23
+ ].join('\n');
24
+ }
25
+ return [
26
+ '## Your message is waiting',
27
+ '',
28
+ `**${notice.computerName}** is offline. We saved your message and will give it to your agent when that computer reconnects. You do not need to resend it.`,
29
+ ].join('\n');
30
+ }
31
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../../src/email/system.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAO5D,MAAM,UAAU,mBAAmB,CAAC,MAA2B;IAK7D,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5F,CAAC;AAED,SAAS,UAAU,CAAC,MAA2B;IAC7C,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACrC,OAAO;YACL,yCAAyC;YACzC,EAAE;YACF,6HAA6H;YAC7H,EAAE;YACF,kCAAkC;SACnC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,OAAO;YACL,oCAAoC;YACpC,EAAE;YACF,+HAA+H;YAC/H,EAAE;YACF,kCAAkC;SACnC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IACD,OAAO;QACL,4BAA4B;QAC5B,EAAE;QACF,KAAK,MAAM,CAAC,YAAY,kIAAkI;KAC3J,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Opaque reply addresses route a response without exposing a user or session id. */
2
+ export declare class EmailThreadAddressing {
3
+ #private;
4
+ readonly inboxAddress: string;
5
+ constructor(secret: string, inboxAddress: string);
6
+ replyAddress(threadId: string): Promise<string>;
7
+ threadHint(recipients: readonly string[]): Promise<string | null>;
8
+ }