@diegoaltoworks/chatter 0.61.0 → 0.63.0

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 (125) hide show
  1. package/README.md +55 -17
  2. package/dist/auth/secretStrength.d.ts +1 -1
  3. package/dist/auth/secretStrength.d.ts.map +1 -1
  4. package/dist/bin/create-apikey.js +45 -28
  5. package/dist/bin/wa-pair.js +113 -46
  6. package/dist/channels/backoff.d.ts +10 -0
  7. package/dist/channels/backoff.d.ts.map +1 -0
  8. package/dist/channels/gates.d.ts +15 -8
  9. package/dist/channels/gates.d.ts.map +1 -1
  10. package/dist/channels/index.d.ts +1 -1
  11. package/dist/channels/index.d.ts.map +1 -1
  12. package/dist/channels/index.js +38 -5
  13. package/dist/channels/index.mjs +37 -5
  14. package/dist/channels/matrix/api.d.ts +25 -6
  15. package/dist/channels/matrix/api.d.ts.map +1 -1
  16. package/dist/channels/matrix/channel.d.ts +4 -56
  17. package/dist/channels/matrix/channel.d.ts.map +1 -1
  18. package/dist/channels/matrix/handler.d.ts +16 -9
  19. package/dist/channels/matrix/handler.d.ts.map +1 -1
  20. package/dist/channels/matrix/index.d.ts +2 -2
  21. package/dist/channels/matrix/index.d.ts.map +1 -1
  22. package/dist/channels/matrix/index.js +196 -40
  23. package/dist/channels/matrix/index.mjs +189 -39
  24. package/dist/channels/matrix/sync.d.ts +4 -1
  25. package/dist/channels/matrix/sync.d.ts.map +1 -1
  26. package/dist/channels/matrix/updates.d.ts +39 -10
  27. package/dist/channels/matrix/updates.d.ts.map +1 -1
  28. package/dist/channels/pipeline.d.ts +14 -14
  29. package/dist/channels/pipeline.d.ts.map +1 -1
  30. package/dist/channels/polling.d.ts +67 -0
  31. package/dist/channels/polling.d.ts.map +1 -0
  32. package/dist/channels/telegram/channel.d.ts +2 -56
  33. package/dist/channels/telegram/channel.d.ts.map +1 -1
  34. package/dist/channels/telegram/index.js +53 -23
  35. package/dist/channels/telegram/index.mjs +53 -23
  36. package/dist/channels/telegram/poll.d.ts +5 -1
  37. package/dist/channels/telegram/poll.d.ts.map +1 -1
  38. package/dist/channels/telegram/webhook.d.ts +2 -10
  39. package/dist/channels/telegram/webhook.d.ts.map +1 -1
  40. package/dist/channels/whatsapp/authState.d.ts +40 -9
  41. package/dist/channels/whatsapp/authState.d.ts.map +1 -1
  42. package/dist/channels/whatsapp/channel.d.ts +39 -4
  43. package/dist/channels/whatsapp/channel.d.ts.map +1 -1
  44. package/dist/channels/whatsapp/inbound.d.ts +23 -25
  45. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  46. package/dist/channels/whatsapp/index.d.ts +3 -3
  47. package/dist/channels/whatsapp/index.d.ts.map +1 -1
  48. package/dist/channels/whatsapp/index.js +188 -66
  49. package/dist/channels/whatsapp/index.mjs +185 -66
  50. package/dist/channels/whatsapp/pairing.d.ts.map +1 -1
  51. package/dist/client/src/Chat.d.ts.map +1 -1
  52. package/dist/client/src/ChatButton.d.ts.map +1 -1
  53. package/dist/client/src/browser.d.ts.map +1 -1
  54. package/dist/core/answer.d.ts +8 -0
  55. package/dist/core/answer.d.ts.map +1 -1
  56. package/dist/core/buildLock.d.ts +52 -0
  57. package/dist/core/buildLock.d.ts.map +1 -0
  58. package/dist/core/pipeline.d.ts +42 -5
  59. package/dist/core/pipeline.d.ts.map +1 -1
  60. package/dist/core/prompts.d.ts +2 -0
  61. package/dist/core/prompts.d.ts.map +1 -1
  62. package/dist/core/retrieval.d.ts +100 -25
  63. package/dist/core/retrieval.d.ts.map +1 -1
  64. package/dist/core/session.d.ts +8 -0
  65. package/dist/core/session.d.ts.map +1 -1
  66. package/dist/flows/index.d.ts +1 -1
  67. package/dist/flows/index.d.ts.map +1 -1
  68. package/dist/flows/index.js +8 -6
  69. package/dist/flows/index.mjs +7 -6
  70. package/dist/flows/intent.d.ts +1 -1
  71. package/dist/flows/intent.d.ts.map +1 -1
  72. package/dist/flows/loader.d.ts +1 -2
  73. package/dist/flows/loader.d.ts.map +1 -1
  74. package/dist/flows/params.d.ts +1 -1
  75. package/dist/flows/params.d.ts.map +1 -1
  76. package/dist/flows/session.d.ts +4 -2
  77. package/dist/flows/session.d.ts.map +1 -1
  78. package/dist/flows/types.d.ts +2 -3
  79. package/dist/flows/types.d.ts.map +1 -1
  80. package/dist/history/index.d.ts +1 -1
  81. package/dist/history/index.d.ts.map +1 -1
  82. package/dist/history/index.js +6 -3
  83. package/dist/history/index.mjs +5 -3
  84. package/dist/history/tursoStore.d.ts +6 -4
  85. package/dist/history/tursoStore.d.ts.map +1 -1
  86. package/dist/images/index.js +2 -2
  87. package/dist/images/index.mjs +2 -2
  88. package/dist/index.d.ts +7 -5
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +596 -295
  91. package/dist/index.mjs +595 -295
  92. package/dist/mcp-server/index.d.ts +5 -5
  93. package/dist/mcp-server/index.d.ts.map +1 -1
  94. package/dist/mcp-server/logger.d.ts +12 -2
  95. package/dist/mcp-server/logger.d.ts.map +1 -1
  96. package/dist/mcp-server/rate-limiter.d.ts +10 -3
  97. package/dist/mcp-server/rate-limiter.d.ts.map +1 -1
  98. package/dist/mcp-server/types.d.ts +20 -5
  99. package/dist/mcp-server/types.d.ts.map +1 -1
  100. package/dist/mcp-server.js +479 -244
  101. package/dist/mcp-server.mjs +485 -244
  102. package/dist/middleware/apiKey.d.ts +10 -0
  103. package/dist/middleware/apiKey.d.ts.map +1 -0
  104. package/dist/middleware/auth.d.ts.map +1 -1
  105. package/dist/middleware/cors.d.ts.map +1 -1
  106. package/dist/middleware/ratelimit.d.ts.map +1 -1
  107. package/dist/middleware/referrer.d.ts.map +1 -1
  108. package/dist/middleware/session.d.ts +1 -1
  109. package/dist/middleware/session.d.ts.map +1 -1
  110. package/dist/routes/demo.d.ts.map +1 -1
  111. package/dist/routes/openai.d.ts.map +1 -1
  112. package/dist/routes/private.d.ts.map +1 -1
  113. package/dist/routes/public.d.ts.map +1 -1
  114. package/dist/scheduler/index.js +1 -1
  115. package/dist/scheduler/index.mjs +1 -1
  116. package/dist/scheduler/scheduler.d.ts +9 -1
  117. package/dist/scheduler/scheduler.d.ts.map +1 -1
  118. package/dist/server.d.ts.map +1 -1
  119. package/dist/server.js +491 -268
  120. package/dist/server.mjs +497 -268
  121. package/dist/shortener.d.ts +5 -4
  122. package/dist/shortener.d.ts.map +1 -1
  123. package/dist/types.d.ts +129 -75
  124. package/dist/types.d.ts.map +1 -1
  125. package/package.json +6 -3
@@ -15,6 +15,7 @@ function redactToken(text, token) {
15
15
  if (!token) return text;
16
16
  return text.split(token).join("***");
17
17
  }
18
+ var DEFAULT_MAX_MEDIA_BYTES = 25 * 1024 * 1024;
18
19
  var MEDIA_MSGTYPE = {
19
20
  image: "m.image",
20
21
  file: "m.file",
@@ -44,6 +45,55 @@ function toMediaRequest(payload) {
44
45
  }
45
46
  return { kind, url: media.url, caption: media.caption, filename: media.filename };
46
47
  }
48
+ function assertHttpsMediaUrl(url) {
49
+ let parsed;
50
+ try {
51
+ parsed = new URL(url);
52
+ } catch {
53
+ throw new TypeError(
54
+ `Matrix sendMedia url must be an mxc:// URI or an https URL - could not parse "${url}"`
55
+ );
56
+ }
57
+ if (parsed.protocol !== "https:") {
58
+ throw new TypeError(
59
+ `Matrix sendMedia url must be an mxc:// URI or an https URL - got "${parsed.protocol}"`
60
+ );
61
+ }
62
+ }
63
+ async function readCappedBytes(response, maxBytes) {
64
+ const declared = Number(response.headers.get("content-length"));
65
+ if (Number.isFinite(declared) && declared > maxBytes) {
66
+ throw new Error(`media source declares ${declared} bytes, over the ${maxBytes} byte cap`);
67
+ }
68
+ const reader = response.body?.getReader();
69
+ if (!reader) {
70
+ const bytes2 = new Uint8Array(await response.arrayBuffer());
71
+ if (bytes2.byteLength > maxBytes) {
72
+ throw new Error(`media source is ${bytes2.byteLength} bytes, over the ${maxBytes} byte cap`);
73
+ }
74
+ return bytes2;
75
+ }
76
+ const chunks = [];
77
+ let total = 0;
78
+ while (true) {
79
+ const { done, value } = await reader.read();
80
+ if (done) break;
81
+ if (!value) continue;
82
+ total += value.byteLength;
83
+ if (total > maxBytes) {
84
+ await reader.cancel();
85
+ throw new Error(`media source exceeds the ${maxBytes} byte cap`);
86
+ }
87
+ chunks.push(value);
88
+ }
89
+ const bytes = new Uint8Array(total);
90
+ let offset = 0;
91
+ for (const chunk of chunks) {
92
+ bytes.set(chunk, offset);
93
+ offset += chunk.byteLength;
94
+ }
95
+ return bytes;
96
+ }
47
97
  function createMatrixApi(config) {
48
98
  const token = config.accessToken;
49
99
  if (!token.trim()) throw new Error("Matrix accessToken is required (e.g. MATRIX_ACCESS_TOKEN)");
@@ -51,6 +101,7 @@ function createMatrixApi(config) {
51
101
  if (!homeserverUrl)
52
102
  throw new Error("Matrix homeserverUrl is required (e.g. https://matrix.example.org)");
53
103
  const doFetch = config.fetch ?? globalThis.fetch;
104
+ const maxMediaBytes = config.maxMediaBytes ?? DEFAULT_MAX_MEDIA_BYTES;
54
105
  async function call(method, path, options) {
55
106
  const search = new URLSearchParams();
56
107
  for (const [key, value] of Object.entries(options?.query ?? {})) {
@@ -144,13 +195,14 @@ function createMatrixApi(config) {
144
195
  const media = toMediaRequest(payload);
145
196
  let contentUri = media.url;
146
197
  if (!contentUri.startsWith("mxc://")) {
198
+ assertHttpsMediaUrl(media.url);
147
199
  let bytes;
148
200
  let contentType = "application/octet-stream";
149
201
  try {
150
202
  const fetched = await doFetch(media.url);
151
203
  if (!fetched.ok) throw new Error(`fetching media source returned HTTP ${fetched.status}`);
152
204
  contentType = fetched.headers.get("content-type") ?? contentType;
153
- bytes = new Uint8Array(await fetched.arrayBuffer());
205
+ bytes = await readCappedBytes(fetched, maxMediaBytes);
154
206
  } catch (error) {
155
207
  throw new MatrixApiError(`could not fetch media source for upload: ${errorText(error)}`);
156
208
  }
@@ -202,6 +254,13 @@ function createMatrixApi(config) {
202
254
  if (error instanceof MatrixApiError && error.errcode === "M_NOT_FOUND") return void 0;
203
255
  throw error;
204
256
  }
257
+ },
258
+ async setAccountData(userId, type, content) {
259
+ await call(
260
+ "PUT",
261
+ `/_matrix/client/v3/user/${encodeURIComponent(userId)}/account_data/${encodeURIComponent(type)}`,
262
+ { body: content }
263
+ );
205
264
  }
206
265
  };
207
266
  }
@@ -294,7 +353,8 @@ async function answerOnce({
294
353
  async function applyTransformReply(transformReply, input, logger) {
295
354
  if (!transformReply) return input.text;
296
355
  try {
297
- return await transformReply(input);
356
+ const result = await transformReply(input);
357
+ return typeof result === "string" ? result : null;
298
358
  } catch (error) {
299
359
  logger?.error(
300
360
  `transformReply threw for channel "${input.channel}"; sending the original reply`,
@@ -356,6 +416,7 @@ async function prepareChat({
356
416
  sender,
357
417
  rewriteQuery,
358
418
  rerankContext,
419
+ fallbackFn,
359
420
  logger
360
421
  }) {
361
422
  const lastUserMsg = lastUserMessage(messages);
@@ -372,21 +433,39 @@ async function prepareChat({
372
433
  logger?.error("rewriteQuery threw; falling back to the original query", error);
373
434
  }
374
435
  }
375
- let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
436
+ const resolvedBuckets = buckets ?? defaultBuckets(mode);
437
+ const retrievedChunks = await store.query(query, topK, resolvedBuckets);
438
+ let ctx = retrievedChunks;
376
439
  if (rerankContext) {
377
440
  try {
378
- const reranked = await rerankContext({ query, chunks: ctx });
441
+ const reranked = await rerankContext({ query, chunks: [...ctx] });
379
442
  if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
380
443
  } catch (error) {
381
444
  logger?.error("rerankContext threw; falling back to the original chunks", error);
382
445
  }
383
446
  }
447
+ let fallback;
448
+ if (ctx.length === 0 && fallbackFn) {
449
+ try {
450
+ const guidance = await fallbackFn({
451
+ query,
452
+ mode,
453
+ buckets: resolvedBuckets,
454
+ sender,
455
+ retrievedChunks
456
+ });
457
+ if (typeof guidance === "string" && guidance.trim()) fallback = guidance.trim();
458
+ } catch (error) {
459
+ logger?.error("fallbackFn threw; proceeding without fallback guidance", error);
460
+ }
461
+ }
384
462
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
385
463
  const hint = channelHint?.trim();
386
464
  const system = [
387
465
  prompts.baseSystemRules,
388
466
  persona,
389
467
  ...hint ? [hint] : [],
468
+ ...fallback ? [fallback] : [],
390
469
  `${label}:
391
470
  ${ctx.join("\n\n")}`
392
471
  ].join("\n\n");
@@ -394,7 +473,7 @@ ${ctx.join("\n\n")}`
394
473
  }
395
474
 
396
475
  // src/history/compaction.ts
397
- var DEFAULT_TIMEOUT_MS = 8e3;
476
+ var COMPACTION_TIMEOUT_MS = 8e3;
398
477
  var LOAD_ALL = Number.MAX_SAFE_INTEGER;
399
478
  var SUMMARY_PREFIX = "Summary of earlier conversation: ";
400
479
  function defaultSummarize(client, model) {
@@ -422,7 +501,7 @@ function createHistoryCompactor(deps, options) {
422
501
  throw new Error(`Invalid history compaction keep: ${keep}`);
423
502
  }
424
503
  const summarize = options.summarize ?? defaultSummarize(deps.client, options.model);
425
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
504
+ const timeoutMs = options.timeoutMs ?? COMPACTION_TIMEOUT_MS;
426
505
  return {
427
506
  async maybeCompact(store, conversationId) {
428
507
  const turns = await store.load(conversationId, LOAD_ALL);
@@ -513,6 +592,16 @@ function underReplyRateLimit(msg, limiters) {
513
592
  }
514
593
 
515
594
  // src/channels/pipeline.ts
595
+ function resolveBrainHooks(config, fallback) {
596
+ return {
597
+ answerFn: config.answerFn ?? fallback?.answerFn,
598
+ bucketsFor: config.bucketsFor ?? fallback?.bucketsFor,
599
+ rewriteQuery: config.rewriteQuery ?? fallback?.rewriteQuery,
600
+ rerankContext: config.rerankContext ?? fallback?.rerankContext,
601
+ fallbackFn: config.fallbackFn ?? fallback?.fallbackFn,
602
+ transformReply: config.transformReply ?? fallback?.transformReply
603
+ };
604
+ }
516
605
  var DEFAULT_HISTORY_LIMIT = 20;
517
606
  var DEFAULT_GROUP_RATE_LIMIT = { max: 30, windowMs: 60 * 60 * 1e3 };
518
607
  var DEFAULT_DM_RATE_LIMIT = { max: 20, windowMs: 60 * 60 * 1e3 };
@@ -597,6 +686,7 @@ function createInboundPipeline(deps, config) {
597
686
  sender,
598
687
  rewriteQuery: config.rewriteQuery,
599
688
  rerankContext: config.rerankContext,
689
+ fallbackFn: config.fallbackFn,
600
690
  logger: config.logger
601
691
  });
602
692
  const { content: produced } = await answerOnce({
@@ -633,6 +723,14 @@ function createInboundPipeline(deps, config) {
633
723
  };
634
724
  }
635
725
 
726
+ // src/channels/polling.ts
727
+ function defaultSleep(ms) {
728
+ return new Promise((resolve) => {
729
+ const timer = setTimeout(resolve, ms);
730
+ if (typeof timer === "object" && "unref" in timer) timer.unref();
731
+ });
732
+ }
733
+
636
734
  // src/channels/matrix/updates.ts
637
735
  function messageText(content) {
638
736
  return content.body ?? "";
@@ -668,23 +766,44 @@ function recordSentEventId(sentEventIds, eventId) {
668
766
  if (oldest !== void 0) sentEventIds.delete(oldest);
669
767
  }
670
768
  }
671
- function directRoomIds(accountDataEvents) {
769
+ function toDirectMapping(content) {
770
+ const mapping = {};
771
+ for (const [peer, value] of Object.entries(content ?? {})) {
772
+ if (!Array.isArray(value)) continue;
773
+ const rooms = value.filter((roomId) => typeof roomId === "string");
774
+ if (rooms.length > 0) mapping[peer] = rooms;
775
+ }
776
+ return mapping;
777
+ }
778
+ function directMappingRooms(mapping) {
779
+ return new Set(Object.values(mapping).flat());
780
+ }
781
+ function directMappingFromEvents(accountDataEvents) {
672
782
  const direct = accountDataEvents.find((event) => event.type === "m.direct");
673
- const mapping = direct?.content ?? {};
674
- const rooms = /* @__PURE__ */ new Set();
675
- for (const value of Object.values(mapping)) {
676
- if (Array.isArray(value)) {
677
- for (const roomId of value) {
678
- if (typeof roomId === "string") rooms.add(roomId);
679
- }
680
- }
783
+ return direct ? toDirectMapping(direct.content) : void 0;
784
+ }
785
+ function directRoomIds(accountDataEvents) {
786
+ return directMappingRooms(directMappingFromEvents(accountDataEvents) ?? {});
787
+ }
788
+ function withDirectRoom(mapping, peer, roomId) {
789
+ const existing = mapping[peer] ?? [];
790
+ if (existing.includes(roomId)) return void 0;
791
+ return { ...mapping, [peer]: [...existing, roomId] };
792
+ }
793
+ function directInviteFrom(room, me) {
794
+ for (const event of room.invite_state?.events ?? []) {
795
+ if (event.type !== "m.room.member" || event.state_key !== me.userId) continue;
796
+ const content = event.content ?? {};
797
+ if (content.membership !== "invite") continue;
798
+ if (content.is_direct === true) return event.sender;
681
799
  }
682
- return rooms;
800
+ return void 0;
683
801
  }
684
802
  function toChannelMessage(roomId, event, me, directRooms, sentEventIds) {
685
803
  if (event.type !== "m.room.message") return void 0;
686
804
  const content = event.content;
687
805
  if (!content.msgtype) return void 0;
806
+ if (content["m.relates_to"]?.rel_type === "m.replace") return void 0;
688
807
  return {
689
808
  chatId: roomId,
690
809
  senderId: event.sender,
@@ -713,19 +832,44 @@ function createMatrixSyncHandler(deps) {
713
832
  const { api, me, pipeline, allowedChats, sentEventIds, logger, label } = deps;
714
833
  const autoJoin = deps.autoJoin ?? true;
715
834
  const loggedUnallowedChats = /* @__PURE__ */ new Set();
716
- let directRooms = new Set(deps.initialDirectRooms ?? []);
835
+ const loggedDeclinedInvites = /* @__PURE__ */ new Set();
836
+ let directMapping = { ...deps.initialDirect ?? {} };
837
+ let directRooms = directMappingRooms(directMapping);
838
+ async function rememberDirectRoom(peer, roomId) {
839
+ directRooms.add(roomId);
840
+ const merged = withDirectRoom(directMapping, peer, roomId);
841
+ if (!merged) return;
842
+ directMapping = merged;
843
+ try {
844
+ await api.setAccountData(me.userId, "m.direct", directMapping);
845
+ } catch (error) {
846
+ logger.warn(`${label}: could not record ${roomId} in m.direct:`, error);
847
+ }
848
+ }
717
849
  return async function handleSync(response) {
718
- if (response.account_data?.events?.some((event) => event.type === "m.direct")) {
719
- directRooms = directRoomIds(response.account_data.events);
850
+ const syncedDirect = directMappingFromEvents(response.account_data?.events ?? []);
851
+ if (syncedDirect) {
852
+ directMapping = syncedDirect;
853
+ directRooms = directMappingRooms(directMapping);
720
854
  }
721
855
  if (autoJoin) {
722
- for (const roomId of Object.keys(response.rooms?.invite ?? {})) {
856
+ for (const [roomId, room] of Object.entries(response.rooms?.invite ?? {})) {
857
+ if (allowedChats.length > 0 && !allowedChats.includes(roomId)) {
858
+ if (!loggedDeclinedInvites.has(roomId)) {
859
+ loggedDeclinedInvites.add(roomId);
860
+ logger.warn(`${label}: left invite to ${roomId} pending - not in allowedChats`);
861
+ }
862
+ continue;
863
+ }
864
+ const directInviter = directInviteFrom(room, me);
723
865
  try {
724
866
  await api.joinRoom(roomId);
725
867
  logger.info(`${label}: joined ${roomId} (accepted invite)`);
726
868
  } catch (error) {
727
869
  logger.warn(`${label}: failed to join ${roomId}:`, error);
870
+ continue;
728
871
  }
872
+ if (directInviter) await rememberDirectRoom(directInviter, roomId);
729
873
  }
730
874
  }
731
875
  for (const [roomId, room] of Object.entries(response.rooms?.join ?? {})) {
@@ -774,11 +918,16 @@ function createMatrixSender(api, sentEventIds) {
774
918
  };
775
919
  }
776
920
 
921
+ // src/channels/backoff.ts
922
+ function exponentialBackoffMs(baseMs, maxMs, n) {
923
+ return Math.min(baseMs * 2 ** Math.max(0, Math.min(n, 20)), maxMs);
924
+ }
925
+
777
926
  // src/channels/matrix/sync.ts
778
927
  var BASE_DELAY_MS = 1e3;
779
928
  var MAX_DELAY_MS = 6e4;
780
929
  function syncBackoffMs(failures) {
781
- return Math.min(BASE_DELAY_MS * 2 ** Math.min(failures, 20), MAX_DELAY_MS);
930
+ return exponentialBackoffMs(BASE_DELAY_MS, MAX_DELAY_MS, failures);
782
931
  }
783
932
  function retryDelayMs(error, failures) {
784
933
  const retryAfterMs = error instanceof MatrixApiError ? error.retryAfterMs : void 0;
@@ -802,9 +951,9 @@ async function runMatrixSyncLoop(deps) {
802
951
  continue;
803
952
  }
804
953
  failures = 0;
954
+ if (deps.isStopped()) break;
805
955
  since = response.next_batch;
806
956
  deps.onSince?.(since);
807
- if (deps.isStopped()) break;
808
957
  try {
809
958
  await deps.handleSync(response);
810
959
  } catch (error) {
@@ -816,12 +965,6 @@ async function runMatrixSyncLoop(deps) {
816
965
 
817
966
  // src/channels/matrix/channel.ts
818
967
  var DEFAULT_SYNC_TIMEOUT_MS = 3e4;
819
- function defaultSleep(ms) {
820
- return new Promise((resolve) => {
821
- const timer = setTimeout(resolve, ms);
822
- if (typeof timer === "object" && "unref" in timer) timer.unref();
823
- });
824
- }
825
968
  function createMatrixChannel(config) {
826
969
  const channelName = config.name ?? "matrix";
827
970
  const allowedChats = config.allowedChats ?? [];
@@ -829,6 +972,7 @@ function createMatrixChannel(config) {
829
972
  const syncTimeoutMs = config.syncTimeoutMs ?? DEFAULT_SYNC_TIMEOUT_MS;
830
973
  let stopped = false;
831
974
  let registered = false;
975
+ let abort;
832
976
  let senders;
833
977
  const sentEventIds = /* @__PURE__ */ new Set();
834
978
  return {
@@ -838,14 +982,15 @@ function createMatrixChannel(config) {
838
982
  const api = config.api ?? createMatrixApi({
839
983
  homeserverUrl: config.homeserverUrl,
840
984
  accessToken: config.accessToken,
841
- fetch: config.fetch
985
+ fetch: config.fetch,
986
+ maxMediaBytes: config.maxMediaBytes
842
987
  });
843
988
  const me = await api.whoami();
844
989
  const label = `Matrix[${me.userId}]`;
845
- let initialDirectRooms;
990
+ let initialDirect;
846
991
  try {
847
992
  const direct = await api.getAccountData(me.userId, "m.direct");
848
- if (direct) initialDirectRooms = directRoomIds([{ type: "m.direct", content: direct }]);
993
+ if (direct) initialDirect = toDirectMapping(direct);
849
994
  } catch (error) {
850
995
  log.warn(`${label}: could not load m.direct account data at start:`, error);
851
996
  }
@@ -856,11 +1001,7 @@ function createMatrixChannel(config) {
856
1001
  { client: deps.client, store: deps.store, prompts: deps.prompts },
857
1002
  {
858
1003
  channel: channelName,
859
- answerFn: config.answerFn ?? deps.config.answerFn,
860
- bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
861
- rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
862
- rerankContext: config.rerankContext ?? deps.config.rerankContext,
863
- transformReply: config.transformReply ?? deps.config.transformReply,
1004
+ ...resolveBrainHooks(config, deps.config),
864
1005
  model: config.model,
865
1006
  channelHint: config.channelHint ?? "Channel: Matrix.",
866
1007
  personaResolver: config.personaResolver,
@@ -883,12 +1024,14 @@ function createMatrixChannel(config) {
883
1024
  allowedChats,
884
1025
  sentEventIds,
885
1026
  autoJoin: config.autoJoin,
886
- initialDirectRooms,
1027
+ initialDirect,
887
1028
  logger: log,
888
1029
  label
889
1030
  });
1031
+ abort = new AbortController();
1032
+ const signal = abort.signal;
890
1033
  void runMatrixSyncLoop({
891
- sync: (since) => api.sync({ since, timeoutMs: syncTimeoutMs }),
1034
+ sync: (since) => api.sync({ since, timeoutMs: syncTimeoutMs, signal }),
892
1035
  handleSync,
893
1036
  isStopped: () => stopped,
894
1037
  sleep,
@@ -901,6 +1044,7 @@ function createMatrixChannel(config) {
901
1044
  },
902
1045
  stop() {
903
1046
  stopped = true;
1047
+ abort?.abort();
904
1048
  if (registered) {
905
1049
  senders?.unregister(channelName);
906
1050
  registered = false;
@@ -909,12 +1053,16 @@ function createMatrixChannel(config) {
909
1053
  };
910
1054
  }
911
1055
  export {
1056
+ DEFAULT_MAX_MEDIA_BYTES,
912
1057
  MAX_TRACKED_SENT_EVENTS,
913
1058
  MatrixApiError,
914
1059
  createMatrixApi,
915
1060
  createMatrixChannel,
916
1061
  createMatrixSender,
917
1062
  createMatrixSyncHandler,
1063
+ directInviteFrom,
1064
+ directMappingFromEvents,
1065
+ directMappingRooms,
918
1066
  directRoomIds,
919
1067
  isReplyToBot,
920
1068
  matrixSenderKey,
@@ -925,5 +1073,7 @@ export {
925
1073
  retryDelayMs,
926
1074
  runMatrixSyncLoop,
927
1075
  syncBackoffMs,
928
- toChannelMessage
1076
+ toChannelMessage,
1077
+ toDirectMapping,
1078
+ withDirectRoom
929
1079
  };
@@ -44,7 +44,10 @@ export interface MatrixSyncLoopDeps {
44
44
  * The token advances BEFORE the batch is handled, matching the Telegram
45
45
  * poll loop's offset-first ordering: a handler that throws on one bad
46
46
  * batch must not make the homeserver redeliver it forever, which would
47
- * wedge the loop on a single poison event and starve every later one.
47
+ * wedge the loop on a single poison event and starve every later one. It
48
+ * advances only for a batch this loop is actually going to handle, though:
49
+ * a stop that lands between the sync and the handler leaves the token where
50
+ * it was, so a restart resumes at the unhandled batch rather than past it.
48
51
  */
49
52
  export declare function runMatrixSyncLoop(deps: MatrixSyncLoopDeps): Promise<string | undefined>;
50
53
  //# sourceMappingURL=sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/channels/matrix/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAKhE,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC7D,0FAA0F;IAC1F,UAAU,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,+HAA+H;IAC/H,SAAS,IAAI,OAAO,CAAC;IACrB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiC7F"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/channels/matrix/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAKhE,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC7D,0FAA0F;IAC1F,UAAU,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,+HAA+H;IAC/H,SAAS,IAAI,OAAO,CAAC;IACrB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoC7F"}
@@ -12,7 +12,7 @@
12
12
  * those booleans (`decideChannelAction`) is identical for every channel.
13
13
  */
14
14
  import type { ChannelMessage } from "../gates";
15
- import type { MatrixAccountDataEvent, MatrixEvent } from "./api";
15
+ import type { MatrixAccountDataEvent, MatrixEvent, MatrixInvitedRoom } from "./api";
16
16
  /** The bot's own identity, as `whoami` reports it. */
17
17
  export interface MatrixIdentity {
18
18
  userId: string;
@@ -24,15 +24,19 @@ interface MatrixMentions {
24
24
  interface MatrixInReplyTo {
25
25
  event_id?: string;
26
26
  }
27
+ interface MatrixRelatesTo {
28
+ /** `m.replace` for an edit, `m.annotation` for a reaction, `m.thread` for a threaded message. Absent on a plain reply. */
29
+ rel_type?: string;
30
+ event_id?: string;
31
+ "m.in_reply_to"?: MatrixInReplyTo;
32
+ }
27
33
  interface MatrixMessageContent {
28
34
  msgtype?: string;
29
35
  body?: string;
30
36
  format?: string;
31
37
  formatted_body?: string;
32
38
  "m.mentions"?: MatrixMentions;
33
- "m.relates_to"?: {
34
- "m.in_reply_to"?: MatrixInReplyTo;
35
- };
39
+ "m.relates_to"?: MatrixRelatesTo;
36
40
  }
37
41
  /** The message text, or "" for an event this channel doesn't render as text (a redaction, a non-text msgtype with no body). */
38
42
  export declare function messageText(content: MatrixMessageContent): string;
@@ -50,18 +54,43 @@ export declare const MAX_TRACKED_SENT_EVENTS = 500;
50
54
  /** Records `eventId` as one of ours, evicting the oldest entry first once the set is at capacity (`Set` iterates in insertion order, so the first value is the oldest). */
51
55
  export declare function recordSentEventId(sentEventIds: Set<string>, eventId: string): void;
52
56
  /**
53
- * Room ids the homeserver's `m.direct` account data lists as direct
54
- * messages, across every peer — the canonical way a Matrix client (and so a
55
- * bot) tells a DM from a group: there is no per-room "this is a DM" flag.
56
- * A room absent from this set is treated as a group and needs addressing,
57
- * the safer default when a client never populated `m.direct` for it.
57
+ * The `m.direct` account-data document: peer user id -> the rooms that are
58
+ * direct messages with that peer. Matrix has no per-room "this is a DM"
59
+ * flag, so this mapping is the only thing that tells one from a group.
58
60
  */
61
+ export type MatrixDirectMapping = Record<string, string[]>;
62
+ /** Raw `m.direct` content (whatever the homeserver stored) narrowed to peer -> room ids, dropping any entry that isn't a non-empty array of strings. */
63
+ export declare function toDirectMapping(content: Record<string, unknown> | undefined): MatrixDirectMapping;
64
+ /** Every room id in a mapping, across every peer. A room absent from it is treated as a group and needs addressing, the safer default. */
65
+ export declare function directMappingRooms(mapping: MatrixDirectMapping): Set<string>;
66
+ /** The `m.direct` mapping carried by a sync batch's account data, or `undefined` when this batch says nothing about it (an unrelated account-data change must not be read as "no DMs"). */
67
+ export declare function directMappingFromEvents(accountDataEvents: MatrixAccountDataEvent[]): MatrixDirectMapping | undefined;
68
+ /** Room ids the homeserver's `m.direct` account data lists as direct messages, across every peer. */
59
69
  export declare function directRoomIds(accountDataEvents: MatrixAccountDataEvent[]): Set<string>;
70
+ /**
71
+ * `mapping` with `roomId` recorded against `peer`, or `undefined` when it is
72
+ * already there, which the caller uses to skip a pointless account-data
73
+ * write. Never mutates the input: `m.direct` is a whole-document PUT, so the
74
+ * merged copy is what gets sent.
75
+ */
76
+ export declare function withDirectRoom(mapping: MatrixDirectMapping, peer: string, roomId: string): MatrixDirectMapping | undefined;
77
+ /**
78
+ * The inviting user id when this invite is for a direct-message room, else
79
+ * `undefined`.
80
+ *
81
+ * A client opening a DM sets `is_direct: true` on the `m.room.member` invite
82
+ * it sends the bot, and records the room in ITS OWN `m.direct`, never in the
83
+ * bot's. The invite is therefore the only moment the bot is told a room is a
84
+ * DM; `./handler` seeds its DM set from this and writes the room into the
85
+ * bot's own `m.direct` so a later restart still knows.
86
+ */
87
+ export declare function directInviteFrom(room: MatrixInvitedRoom, me: MatrixIdentity): string | undefined;
60
88
  /**
61
89
  * The {@link ChannelMessage} for a timeline event, or `undefined` when
62
90
  * there is nothing for a chat pipeline to look at: not an `m.room.message`
63
91
  * (a reaction, a membership change, an encrypted event this channel can't
64
- * decrypt), or one with no `msgtype` at all (a redacted message).
92
+ * decrypt), one with no `msgtype` at all (a redacted message), or an edit of
93
+ * a message that was already delivered (see below).
65
94
  */
66
95
  export declare function toChannelMessage(roomId: string, event: MatrixEvent, me: MatrixIdentity, directRooms: ReadonlySet<string>, sentEventIds: ReadonlySet<string>): ChannelMessage | undefined;
67
96
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../../src/channels/matrix/updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEjE,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,cAAc,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;CACxD;AAED,+HAA+H;AAC/H,wBAAgB,WAAW,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAEjE;AAiCD,wBAAgB,WAAW,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAQtF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,OAAO,CAGT;AAED,yKAAyK;AACzK,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C,2KAA2K;AAC3K,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMlF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAYtF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,cAAc,EAClB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,cAAc,GAAG,SAAS,CAe5B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD"}
1
+ {"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../../src/channels/matrix/updates.ts"],"names":[],"mappings":"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"}
@@ -14,15 +14,23 @@
14
14
  * re-derive it. See docs/build-a-channel.md for a worked example.
15
15
  */
16
16
  import type OpenAI from "openai";
17
- import type { AnswerFn, TransformReply } from "../core/answer";
18
- import type { BucketsFor } from "../core/buckets";
19
17
  import type { Logger } from "../core/logger";
20
- import { type RerankContext, type RewriteQuery } from "../core/pipeline";
21
18
  import type { PromptLoader } from "../core/prompts";
22
- import type { VectorStore } from "../core/retrieval";
19
+ import type { Retriever } from "../core/retrieval";
23
20
  import type { HistoryCompactionOptions } from "../history/compaction";
24
21
  import type { HistoryStore } from "../history/types";
22
+ import type { BrainHooks } from "../types";
25
23
  import { type ChannelMessage } from "./gates";
24
+ /**
25
+ * Resolves the brain hooks (`answerFn`, `bucketsFor`, `rewriteQuery`,
26
+ * `rerankContext`, `fallbackFn`, `transformReply`) for one channel: each
27
+ * field from `config` wins when set, falling back to the matching field on
28
+ * `fallback` (typically `deps.config`, the server-level `ChatterConfig`).
29
+ * Every channel that layers its own hooks over the server's uses this
30
+ * instead of hand-rolling the same six `??` lines: see `../telegram`,
31
+ * `../matrix` and `../whatsapp/inbound`.
32
+ */
33
+ export declare function resolveBrainHooks(config: BrainHooks, fallback?: BrainHooks): BrainHooks;
26
34
  /** Where a pipeline turn's output goes. Distinct from `ChannelSender` (`./senders`) — a transport implementing this may thread/quote the chat answer without doing the same for a plain gate acknowledgement. */
27
35
  export interface InboundReplySender {
28
36
  /** Deliver the chat answer for `chatId`. */
@@ -32,23 +40,15 @@ export interface InboundReplySender {
32
40
  }
33
41
  export interface InboundPipelineDeps {
34
42
  client: OpenAI;
35
- store: VectorStore;
43
+ store: Retriever;
36
44
  prompts: PromptLoader;
37
45
  }
38
- export interface InboundPipelineConfig {
46
+ export interface InboundPipelineConfig extends BrainHooks {
39
47
  /**
40
48
  * This channel's identity for `transformReply` — e.g. "whatsapp",
41
49
  * "telegram". @default "channel"
42
50
  */
43
51
  channel?: string;
44
- answerFn?: AnswerFn;
45
- bucketsFor?: BucketsFor;
46
- /** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. */
47
- rewriteQuery?: RewriteQuery;
48
- /** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. */
49
- rerankContext?: RerankContext;
50
- /** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
51
- transformReply?: TransformReply;
52
52
  model?: string;
53
53
  /** Extra system-prompt section describing the delivery channel; passed through to `prepareChat`. */
54
54
  channelHint?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}