@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,56 @@
1
+ import { ChannelsError } from '../errors.js';
2
+ import { identifier } from '../validation.js';
3
+ export function createMachineInboxService(repository, events, options = {}) {
4
+ const now = options.now ?? (() => new Date());
5
+ return Object.freeze({
6
+ for(principal) {
7
+ const scopes = new Set(principal.scopes);
8
+ if (!scopes.has('channels:receive')) {
9
+ throw new ChannelsError('forbidden', 'Missing scope: channels:receive');
10
+ }
11
+ const userId = identifier(principal.userId, 'principal.userId');
12
+ const computerId = principal.computerId
13
+ ? identifier(principal.computerId, 'principal.computerId')
14
+ : null;
15
+ if (!computerId)
16
+ throw new ChannelsError('forbidden', 'Machine identity is required');
17
+ return Object.freeze({
18
+ claim(input = {}) {
19
+ const leaseSeconds = integer(input.leaseSeconds ?? 60, 30, 900, 'leaseSeconds');
20
+ return repository.claim(userId, computerId, 1, leaseSeconds);
21
+ },
22
+ accept(message) {
23
+ return repository.accept(userId, computerId, lease(message), now());
24
+ },
25
+ release(message, error) {
26
+ return repository.release(userId, computerId, lease(message), bounded(error, 'error', 1, 500), now());
27
+ },
28
+ events(signal) {
29
+ return events(userId, computerId, signal);
30
+ },
31
+ });
32
+ },
33
+ });
34
+ }
35
+ function lease(value) {
36
+ return Object.freeze({
37
+ id: identifier(value.id, 'message.id'),
38
+ leaseToken: identifier(value.leaseToken, 'message.leaseToken'),
39
+ });
40
+ }
41
+ function integer(value, minimum, maximum, name) {
42
+ if (!Number.isInteger(value) || value < minimum || value > maximum) {
43
+ throw new ChannelsError('validation', `${name} must be an integer from ${minimum} to ${maximum}`);
44
+ }
45
+ return value;
46
+ }
47
+ function bounded(value, name, minimum, maximum) {
48
+ if (typeof value !== 'string')
49
+ throw new ChannelsError('validation', `${name} must be a string`);
50
+ const clean = value.replace(/[\r\n]+/g, ' ').trim();
51
+ if (clean.length < minimum || clean.length > maximum) {
52
+ throw new ChannelsError('validation', `${name} must be ${minimum}-${maximum} characters`);
53
+ }
54
+ return clean;
55
+ }
56
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/inbox/service.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,18 @@
1
1
  export * from './contract.js';
2
2
  export * from './errors.js';
3
+ export * from './api-error.js';
3
4
  export * from './validation.js';
4
5
  export * from './service.js';
5
6
  export * from './worker.js';
6
7
  export * from './http.js';
7
8
  export * from './client.js';
9
+ export * from './command-surface.js';
10
+ export * from './email/notification.js';
11
+ export * from './email/thread-token.js';
12
+ export * from './email/inbound.js';
13
+ export * from './email/system.js';
14
+ export * from './inbox/contract.js';
15
+ export * from './inbox/service.js';
16
+ export * from './inbox/client.js';
17
+ export * from './inbox/runner.js';
18
+ export * from './inbox/events.js';
package/dist/src/index.js CHANGED
@@ -1,8 +1,19 @@
1
1
  export * from './contract.js';
2
2
  export * from './errors.js';
3
+ export * from './api-error.js';
3
4
  export * from './validation.js';
4
5
  export * from './service.js';
5
6
  export * from './worker.js';
6
7
  export * from './http.js';
7
8
  export * from './client.js';
9
+ export * from './command-surface.js';
10
+ export * from './email/notification.js';
11
+ export * from './email/thread-token.js';
12
+ export * from './email/inbound.js';
13
+ export * from './email/system.js';
14
+ export * from './inbox/contract.js';
15
+ export * from './inbox/service.js';
16
+ export * from './inbox/client.js';
17
+ export * from './inbox/runner.js';
18
+ export * from './inbox/events.js';
8
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
package/dist/src/mcp.js CHANGED
@@ -1,66 +1,34 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod/v3';
3
+ import { channelsCommandDefinitions, createChannelsCommands } from './command-surface.js';
3
4
  const outputSchema = { result: z.unknown() };
4
5
  export function createChannelsMcpServer(channels) {
5
6
  const server = new McpServer({ name: 'amalgm-channels-mcp-server', version: '0.1.0' });
6
- server.registerTool('amalgm_channels_notify_user', {
7
- title: 'Notify current user',
8
- description: 'Queue one durable notification to the authenticated Amalgm user. The caller cannot select another recipient. Supply an idempotency key when retrying the same logical message.',
9
- inputSchema: {
10
- message: z.string().min(1).max(4_000).describe('Notification text.'),
11
- title: z.string().min(1).max(160).optional().describe('Short notification title.'),
12
- channel: z.enum(['auto', 'email']).optional().describe('Delivery preference. Auto currently resolves to email.'),
13
- idempotency_key: z.string().min(1).max(200).optional().describe('Stable key for safe retries of this exact notification.'),
14
- },
15
- outputSchema,
16
- annotations: {
17
- readOnlyHint: false,
18
- destructiveHint: false,
19
- idempotentHint: true,
20
- openWorldHint: true,
21
- },
22
- }, async ({ message, title, channel, idempotency_key }) => result(await channels.notify({
23
- message,
24
- ...(title === undefined ? {} : { title }),
25
- ...(channel === undefined ? {} : { channel }),
26
- ...(idempotency_key === undefined ? {} : { idempotencyKey: idempotency_key }),
27
- })));
28
- server.registerTool('amalgm_channels_list_deliveries', {
29
- title: 'List notification deliveries',
30
- description: 'List the authenticated user\'s durable notification delivery history.',
31
- inputSchema: {
32
- status: z.enum(['pending', 'sending', 'delivered', 'failed']).optional(),
33
- limit: z.number().int().min(1).max(100).optional(),
34
- offset: z.number().int().min(0).optional(),
35
- },
36
- outputSchema,
37
- annotations: readOnly(),
38
- }, async ({ status, limit, offset }) => result(await channels.list({
39
- ...(status === undefined ? {} : { status }),
40
- ...(limit === undefined ? {} : { limit }),
41
- ...(offset === undefined ? {} : { offset }),
42
- })));
43
- server.registerTool('amalgm_channels_get_delivery', {
44
- title: 'Get notification delivery',
45
- description: 'Get one durable notification delivery by id for the authenticated user.',
46
- inputSchema: { delivery_id: z.string().min(1).max(200) },
47
- outputSchema,
48
- annotations: readOnly(),
49
- }, async ({ delivery_id }) => result(await channels.get(delivery_id)));
7
+ const commands = createChannelsCommands(channels);
8
+ for (const definition of channelsCommandDefinitions) {
9
+ server.registerTool(definition.mcpName, {
10
+ title: definition.title,
11
+ description: definition.description,
12
+ inputSchema: definition.inputShape,
13
+ outputSchema,
14
+ annotations: definition.annotations,
15
+ }, async (input, extra) => {
16
+ const trustedSessionId = sessionId(extra._meta);
17
+ return result(await commands.execute(definition.name, input, trustedSessionId ? { sessionId: trustedSessionId } : {}));
18
+ });
19
+ }
50
20
  return server;
51
21
  }
22
+ function sessionId(meta) {
23
+ if (!meta || typeof meta !== 'object' || Array.isArray(meta))
24
+ return undefined;
25
+ const value = meta['amalgm/sessionId'];
26
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
27
+ }
52
28
  function result(value) {
53
29
  return {
54
30
  content: [{ type: 'text', text: JSON.stringify(value) }],
55
31
  structuredContent: { result: value },
56
32
  };
57
33
  }
58
- function readOnly() {
59
- return {
60
- readOnlyHint: true,
61
- destructiveHint: false,
62
- idempotentHint: true,
63
- openWorldHint: false,
64
- };
65
- }
66
34
  //# sourceMappingURL=mcp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAG3B,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;AAE7C,MAAM,UAAU,uBAAuB,CAAC,QAAkB;IACxD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,4BAA4B,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAEvF,MAAM,CAAC,YAAY,CAAC,6BAA6B,EAAE;QACjD,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,gLAAgL;QAC7L,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;YACpE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAClF,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;YAChH,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;SAC3H;QACD,YAAY;QACZ,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtF,OAAO;QACP,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAC7C,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;KAC9E,CAAC,CAAC,CAAC,CAAC;IAEL,MAAM,CAAC,YAAY,CAAC,iCAAiC,EAAE;QACrD,KAAK,EAAE,8BAA8B;QACrC,WAAW,EAAE,uEAAuE;QACpF,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;YACxE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;YAClD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;SAC3C;QACD,YAAY;QACZ,WAAW,EAAE,QAAQ,EAAE;KACxB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC;QACjE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3C,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;KAC5C,CAAC,CAAC,CAAC,CAAC;IAEL,MAAM,CAAC,YAAY,CAAC,8BAA8B,EAAE;QAClD,KAAK,EAAE,2BAA2B;QAClC,WAAW,EAAE,yEAAyE;QACtF,WAAW,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACxD,YAAY;QACZ,WAAW,EAAE,QAAQ,EAAE;KACxB,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAEvE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,iBAAiB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;KACrC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ;IACf,OAAO;QACL,YAAY,EAAE,IAAI;QAClB,eAAe,EAAE,KAAK;QACtB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACrB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,OAAO,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE1F,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;AAE7C,MAAM,UAAU,uBAAuB,CAAC,QAAkB;IACxD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,4BAA4B,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IAElD,KAAK,MAAM,UAAU,IAAI,0BAA0B,EAAE,CAAC;QACpD,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,EAAE;YACtC,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,WAAW,EAAE,UAAU,CAAC,WAAW;YACnC,WAAW,EAAE,UAAU,CAAC,UAAU;YAClC,YAAY;YACZ,WAAW,EAAE,UAAU,CAAC,WAAW;SACpC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACxB,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC,MAAM,QAAQ,CAAC,OAAO,CAClC,UAAU,CAAC,IAAI,EACf,KAAK,EACL,gBAAgB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CACxD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAAC,IAAa;IAC9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/E,MAAM,KAAK,GAAI,IAAgC,CAAC,kBAAkB,CAAC,CAAC;IACpE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,iBAAiB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;KACrC,CAAC;AACJ,CAAC"}
@@ -1,14 +1,18 @@
1
1
  import { ChannelsError } from './errors.js';
2
- import { deliveryQuery, identifier, notification } from './validation.js';
2
+ import { deliveryQuery, identifier, sendMessage } from './validation.js';
3
3
  export function createChannelsService(repository) {
4
4
  return Object.freeze({
5
5
  for(principal) {
6
6
  const userId = identifier(principal.userId, 'principal.userId');
7
7
  const scopes = new Set(principal.scopes);
8
8
  return Object.freeze({
9
- async notify(input) {
9
+ async send(input, context = {}) {
10
10
  requireScope(scopes, 'channels:send');
11
- return repository.enqueue(userId, notification(input));
11
+ return repository.enqueue(userId, principal.computerId, sendMessage(input), Object.freeze({
12
+ ...(context.sessionId === undefined
13
+ ? {}
14
+ : { sessionId: identifier(context.sessionId, 'context.sessionId') }),
15
+ }));
12
16
  },
13
17
  async list(query) {
14
18
  requireScope(scopes, 'channels:read');
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE1E,MAAM,UAAU,qBAAqB,CAAC,UAA8B;IAClE,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,CAAC,SAA2B;YAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,MAAM,CAAC,CAAC;YACjD,OAAO,MAAM,CAAC,MAAM,CAAC;gBACnB,KAAK,CAAC,MAAM,CAAC,KAAwC;oBACnD,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;gBACzD,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,KAAuC;oBAChD,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;gBACvD,CAAC;gBACD,KAAK,CAAC,GAAG,CAAC,UAAkB;oBAC1B,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;gBACtE,CAAC;aACF,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,MAA2B,EAAE,QAAgB;IACjE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,kBAAkB,QAAQ,EAAE,CAAC,CAAC;AAChG,CAAC"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEzE,MAAM,UAAU,qBAAqB,CAAC,UAA8B;IAClE,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,CAAC,SAA2B;YAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;YAChE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,MAAM,CAAC,CAAC;YACjD,OAAO,MAAM,CAAC,MAAM,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,KAAsC,EAAE,UAA8B,EAAE;oBACjF,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,OAAO,CACvB,MAAM,EACN,SAAS,CAAC,UAAU,EACpB,WAAW,CAAC,KAAK,CAAC,EAClB,MAAM,CAAC,MAAM,CAAC;wBACZ,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS;4BACjC,CAAC,CAAC,EAAE;4BACJ,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,mBAAmB,CAAC,EAAE,CAAC;qBACvE,CAAC,CACH,CAAC;gBACJ,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,KAAuC;oBAChD,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;gBACvD,CAAC;gBACD,KAAK,CAAC,GAAG,CAAC,UAAkB;oBAC1B,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;oBACtC,OAAO,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;gBACtE,CAAC;aACF,CAAC,CAAC;QACL,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,MAA2B,EAAE,QAAgB;IACjE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,kBAAkB,QAAQ,EAAE,CAAC,CAAC;AAChG,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ListDeliveries, NormalizedListDeliveries, NotifyUserInput } from './contract.js';
2
- export declare function notification(input: NotifyUserInput): Required<NotifyUserInput>;
1
+ import type { ListDeliveries, NormalizedListDeliveries, NormalizedSendMessage, SendMessageInput } from './contract.js';
2
+ export declare function sendMessage(input: SendMessageInput): NormalizedSendMessage;
3
3
  export declare function deliveryQuery(input?: ListDeliveries): NormalizedListDeliveries;
4
4
  export declare function identifier(value: string, name: string): string;
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { ChannelsError } from './errors.js';
3
- const statuses = new Set(['pending', 'sending', 'delivered', 'failed']);
4
- export function notification(input) {
3
+ const statuses = new Set(['pending', 'sending', 'sent', 'delivered', 'failed']);
4
+ export function sendMessage(input) {
5
5
  const message = bounded(input.message, 'message', 1, 4_000);
6
6
  const title = input.title === undefined
7
7
  ? 'Amalgm reminder'
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validation.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { DeliveryRepository, EmailProvider, RecipientDirectory } from './contract.js';
2
+ import type { EmailThreadAddressing } from './email/thread-token.js';
2
3
  export interface ChannelsWorkerOptions {
3
4
  readonly batchSize?: number;
4
5
  readonly leaseSeconds?: number;
@@ -10,6 +11,8 @@ export declare class ChannelsWorker {
10
11
  private readonly repository;
11
12
  private readonly directory;
12
13
  private readonly email;
13
- constructor(repository: DeliveryRepository, directory: RecipientDirectory, email: EmailProvider, options?: ChannelsWorkerOptions);
14
+ private readonly addressing;
15
+ constructor(repository: DeliveryRepository, directory: RecipientDirectory, email: EmailProvider, addressing: EmailThreadAddressing, options?: ChannelsWorkerOptions);
14
16
  drainOnce(): Promise<number>;
17
+ nextWakeAt(): Promise<Date | null>;
15
18
  }
@@ -1,15 +1,18 @@
1
+ import { renderNotificationEmail } from './email/notification.js';
1
2
  export class ChannelsWorker {
2
3
  repository;
3
4
  directory;
4
5
  email;
6
+ addressing;
5
7
  #batchSize;
6
8
  #leaseSeconds;
7
9
  #now;
8
10
  #log;
9
- constructor(repository, directory, email, options = {}) {
11
+ constructor(repository, directory, email, addressing, options = {}) {
10
12
  this.repository = repository;
11
13
  this.directory = directory;
12
14
  this.email = email;
15
+ this.addressing = addressing;
13
16
  this.#batchSize = options.batchSize ?? 20;
14
17
  this.#leaseSeconds = options.leaseSeconds ?? 60;
15
18
  this.#now = options.now ?? (() => new Date());
@@ -23,14 +26,30 @@ export class ChannelsWorker {
23
26
  async #deliver(delivery) {
24
27
  try {
25
28
  const to = await this.directory.emailForUser(delivery.userId);
29
+ const replyTo = await this.addressing.replyAddress(delivery.thread.id);
30
+ const references = [...delivery.thread.references];
31
+ if (delivery.thread.replyToMessageId && !references.includes(delivery.thread.replyToMessageId)) {
32
+ references.push(delivery.thread.replyToMessageId);
33
+ }
26
34
  const result = await this.email.send({
27
35
  to,
28
- subject: delivery.title ?? 'Amalgm reminder',
36
+ subject: delivery.thread.subject,
29
37
  text: delivery.message,
30
- idempotencyKey: delivery.idempotencyKey,
38
+ html: renderNotificationEmail(delivery.message),
39
+ idempotencyKey: `channel-delivery:${delivery.id}`,
40
+ replyTo,
41
+ headers: {
42
+ 'Message-ID': delivery.thread.messageId,
43
+ ...(delivery.thread.replyToMessageId
44
+ ? { 'In-Reply-To': delivery.thread.replyToMessageId }
45
+ : {}),
46
+ ...(references.length ? { References: references.join(' ') } : {}),
47
+ },
48
+ });
49
+ const marked = await this.repository.markSent(delivery, result.providerId, this.#now());
50
+ this.#log(marked ? 'delivery.sent' : 'delivery.lease_lost', {
51
+ deliveryId: delivery.id, attempts: delivery.attempts,
31
52
  });
32
- await this.repository.complete(delivery, result.providerId, this.#now());
33
- this.#log('delivery.completed', { deliveryId: delivery.id, attempts: delivery.attempts });
34
53
  }
35
54
  catch (error) {
36
55
  const message = safeError(error);
@@ -38,6 +57,9 @@ export class ChannelsWorker {
38
57
  this.#log('delivery.failed', { deliveryId: delivery.id, error: message });
39
58
  }
40
59
  }
60
+ nextWakeAt() {
61
+ return this.repository.nextWorkAt();
62
+ }
41
63
  }
42
64
  function safeError(error) {
43
65
  const message = error instanceof Error ? error.message : 'Provider delivery failed';
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,cAAc;IAON,UAAU;IACV,SAAS;IACT,KAAK;IARf,UAAU,CAAS;IACnB,aAAa,CAAS;IACtB,IAAI,CAAa;IACjB,IAAI,CAA4C;IAEzD,YACmB,UAA8B,EAC9B,SAA6B,EAC7B,KAAoB,EACrC,OAAO,GAA0B,EAAE;0BAHlB,UAAU;yBACV,SAAS;qBACT,KAAK;QAGtB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACpF,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,UAAU,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAyB;QACtC,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACnC,EAAE;gBACF,OAAO,EAAE,QAAQ,CAAC,KAAK,IAAI,iBAAiB;gBAC5C,IAAI,EAAE,QAAQ,CAAC,OAAO;gBACtB,cAAc,EAAE,QAAQ,CAAC,cAAc;aACxC,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACzE,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;IACpF,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC"}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAUlE,MAAM,OAAO,cAAc;IAON;IACA;IACA;IACA;IATV,UAAU,CAAS;IACnB,aAAa,CAAS;IACtB,IAAI,CAAa;IACjB,IAAI,CAA4C;IAEzD,YACmB,UAA8B,EAC9B,SAA6B,EAC7B,KAAoB,EACpB,UAAiC,EAClD,UAAiC,EAAE;QAJlB,eAAU,GAAV,UAAU,CAAoB;QAC9B,cAAS,GAAT,SAAS,CAAoB;QAC7B,UAAK,GAAL,KAAK,CAAe;QACpB,eAAU,GAAV,UAAU,CAAuB;QAGlD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACpF,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,UAAU,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAyB;QACtC,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACvE,MAAM,UAAU,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,IAAI,QAAQ,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC/F,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACpD,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACnC,EAAE;gBACF,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO;gBAChC,IAAI,EAAE,QAAQ,CAAC,OAAO;gBACtB,IAAI,EAAE,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC/C,cAAc,EAAE,oBAAoB,QAAQ,CAAC,EAAE,EAAE;gBACjD,OAAO;gBACP,OAAO,EAAE;oBACP,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS;oBACvC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,gBAAgB;wBAClC,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,gBAAgB,EAAE;wBACrD,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnE;aACF,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACxF,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,qBAAqB,EAAE;gBAC1D,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aACrD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3D,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IACtC,CAAC;CACF;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;IACpF,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC"}
package/package.json CHANGED
@@ -1,32 +1,58 @@
1
1
  {
2
2
  "name": "@amalgm/channels",
3
- "version": "0.1.0",
4
- "description": "Amalgm's SDK and hosted service for durable user notification delivery.",
3
+ "version": "0.1.2",
4
+ "description": "Amalgm's SDK and hosted service for durable bidirectional user channels.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/amalgm-inc/amalgm-channels.git"
10
10
  },
11
- "publishConfig": { "access": "public" },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
12
14
  "exports": {
13
- ".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
14
- "./mcp": { "types": "./dist/src/mcp.d.ts", "import": "./dist/src/mcp.js" },
15
- "./host": { "types": "./dist/host/index.d.ts", "import": "./dist/host/index.js" }
15
+ ".": {
16
+ "types": "./dist/src/index.d.ts",
17
+ "import": "./dist/src/index.js"
18
+ },
19
+ "./mcp": {
20
+ "types": "./dist/src/mcp.d.ts",
21
+ "import": "./dist/src/mcp.js"
22
+ },
23
+ "./cli": {
24
+ "types": "./dist/src/cli.d.ts",
25
+ "import": "./dist/src/cli.js"
26
+ },
27
+ "./host": {
28
+ "types": "./dist/host/index.d.ts",
29
+ "import": "./dist/host/index.js"
30
+ }
31
+ },
32
+ "bin": {
33
+ "amalgm-channels": "dist/src/cli-main.js"
16
34
  },
17
- "files": ["dist", "supabase", "skills", "PURPOSE.md", "README.md"],
35
+ "files": [
36
+ "dist",
37
+ "supabase",
38
+ "skills",
39
+ "PURPOSE.md",
40
+ "README.md"
41
+ ],
18
42
  "scripts": {
19
43
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
20
- "build": "npm run clean && tsc -p tsconfig.build.json",
44
+ "build": "npm run clean && tsc -p tsconfig.build.json && tsx scripts/mark-executables.ts",
21
45
  "check": "tsx scripts/check-tree.ts && tsc -p tsconfig.json --noEmit",
22
46
  "test": "tsx --test test/*.test.ts",
23
- "verify": "npm run check && npm test && npm run build",
47
+ "verify": "npm run check && npm test && npm run build && tsx scripts/check-cli-artifact.ts",
24
48
  "prepack": "npm run build",
25
49
  "start": "node dist/host/main.js"
26
50
  },
27
- "engines": { "node": ">=20" },
51
+ "engines": {
52
+ "node": ">=20"
53
+ },
28
54
  "dependencies": {
29
- "@amalgm/core": "0.4.2",
55
+ "@amalgm/core": "0.4.6",
30
56
  "@modelcontextprotocol/sdk": "^1.30.0",
31
57
  "@supabase/supabase-js": "2.57.4",
32
58
  "jose": "^6.1.0",
@@ -35,6 +61,6 @@
35
61
  "devDependencies": {
36
62
  "@types/node": "^26.1.2",
37
63
  "tsx": "^4.23.1",
38
- "typescript": "^7.0.2"
64
+ "typescript": "5.9.3"
39
65
  }
40
66
  }
@@ -1,29 +1,29 @@
1
1
  ---
2
2
  name: amalgm-channels
3
- description: Send a durable notification to the current Amalgm user or inspect their delivery history through the Channels tools.
3
+ description: Deliberately send one discrete message to the current Amalgm user over a selected external channel.
4
4
  ---
5
5
 
6
6
  # Amalgm Channels
7
7
 
8
- Use Channels when the user asks to be notified, reminded, emailed, or contacted
9
- by an automation. Channels always addresses the authenticated current user. Do
10
- not ask for or pass their email address.
8
+ Use Channels when the user asks to be notified, reminded, emailed, or contacted,
9
+ or when an inbound channel message requires a deliberate reply. Channels always
10
+ addresses the authenticated current user. Do not ask for or pass their email
11
+ address.
11
12
 
12
13
  ## Send
13
14
 
14
- Call `amalgm_channels_notify_user` with concise `message` text. Add a short
15
- `title` when it improves recognition. Use `channel: auto` unless the user
16
- explicitly asks for email.
15
+ Call the one tool, `amalgm_channels_send`, with concise `message` text and the
16
+ selected `channel`. Email is currently the supported channel. Add a short
17
+ `title` when it improves recognition.
18
+
19
+ Ordinary assistant output is local and is never delivered through Channels.
20
+ After finishing inbound work, explicitly call the send tool for each complete
21
+ message that should reach the user. Never use it to stream progress fragments.
17
22
 
18
23
  When the notification belongs to an automation run, always pass that durable
19
24
  run id as `idempotency_key`. Retrying the same run will then return the original
20
25
  delivery instead of sending twice.
21
26
 
22
27
  The returned `pending` state means the message is durably queued, not yet
23
- delivered. Use `amalgm_channels_get_delivery` if confirmed delivery matters.
24
-
25
- ## Inspect
26
-
27
- Use `amalgm_channels_list_deliveries` for history and status filtering. Delivery
28
- records expose content, attempts, safe provider status, and timestamps; they do
29
- not expose recipient coordinates or provider credentials.
28
+ delivered. The tool cannot select another recipient, session, or provider
29
+ credential.