@diegoaltoworks/chatter 0.61.0 → 0.62.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.
- package/README.md +55 -17
- package/dist/auth/secretStrength.d.ts +1 -1
- package/dist/auth/secretStrength.d.ts.map +1 -1
- package/dist/bin/create-apikey.js +45 -28
- package/dist/bin/wa-pair.js +113 -46
- package/dist/channels/backoff.d.ts +10 -0
- package/dist/channels/backoff.d.ts.map +1 -0
- package/dist/channels/gates.d.ts +15 -8
- package/dist/channels/gates.d.ts.map +1 -1
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/index.d.ts.map +1 -1
- package/dist/channels/index.js +38 -5
- package/dist/channels/index.mjs +37 -5
- package/dist/channels/matrix/api.d.ts +25 -6
- package/dist/channels/matrix/api.d.ts.map +1 -1
- package/dist/channels/matrix/channel.d.ts +4 -56
- package/dist/channels/matrix/channel.d.ts.map +1 -1
- package/dist/channels/matrix/handler.d.ts +16 -9
- package/dist/channels/matrix/handler.d.ts.map +1 -1
- package/dist/channels/matrix/index.d.ts +2 -2
- package/dist/channels/matrix/index.d.ts.map +1 -1
- package/dist/channels/matrix/index.js +196 -40
- package/dist/channels/matrix/index.mjs +189 -39
- package/dist/channels/matrix/sync.d.ts +4 -1
- package/dist/channels/matrix/sync.d.ts.map +1 -1
- package/dist/channels/matrix/updates.d.ts +39 -10
- package/dist/channels/matrix/updates.d.ts.map +1 -1
- package/dist/channels/pipeline.d.ts +14 -14
- package/dist/channels/pipeline.d.ts.map +1 -1
- package/dist/channels/polling.d.ts +67 -0
- package/dist/channels/polling.d.ts.map +1 -0
- package/dist/channels/telegram/channel.d.ts +2 -56
- package/dist/channels/telegram/channel.d.ts.map +1 -1
- package/dist/channels/telegram/index.js +53 -23
- package/dist/channels/telegram/index.mjs +53 -23
- package/dist/channels/telegram/poll.d.ts +5 -1
- package/dist/channels/telegram/poll.d.ts.map +1 -1
- package/dist/channels/telegram/webhook.d.ts +2 -10
- package/dist/channels/telegram/webhook.d.ts.map +1 -1
- package/dist/channels/whatsapp/authState.d.ts +40 -9
- package/dist/channels/whatsapp/authState.d.ts.map +1 -1
- package/dist/channels/whatsapp/channel.d.ts +25 -4
- package/dist/channels/whatsapp/channel.d.ts.map +1 -1
- package/dist/channels/whatsapp/inbound.d.ts +23 -25
- package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.d.ts +3 -3
- package/dist/channels/whatsapp/index.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.js +123 -41
- package/dist/channels/whatsapp/index.mjs +120 -41
- package/dist/channels/whatsapp/pairing.d.ts.map +1 -1
- package/dist/client/src/Chat.d.ts.map +1 -1
- package/dist/client/src/ChatButton.d.ts.map +1 -1
- package/dist/client/src/browser.d.ts.map +1 -1
- package/dist/core/answer.d.ts +8 -0
- package/dist/core/answer.d.ts.map +1 -1
- package/dist/core/buildLock.d.ts +52 -0
- package/dist/core/buildLock.d.ts.map +1 -0
- package/dist/core/pipeline.d.ts +42 -5
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/prompts.d.ts +2 -0
- package/dist/core/prompts.d.ts.map +1 -1
- package/dist/core/retrieval.d.ts +100 -25
- package/dist/core/retrieval.d.ts.map +1 -1
- package/dist/core/session.d.ts +8 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/flows/index.d.ts +1 -1
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js +8 -6
- package/dist/flows/index.mjs +7 -6
- package/dist/flows/intent.d.ts +1 -1
- package/dist/flows/intent.d.ts.map +1 -1
- package/dist/flows/loader.d.ts +1 -2
- package/dist/flows/loader.d.ts.map +1 -1
- package/dist/flows/params.d.ts +1 -1
- package/dist/flows/params.d.ts.map +1 -1
- package/dist/flows/session.d.ts +4 -2
- package/dist/flows/session.d.ts.map +1 -1
- package/dist/flows/types.d.ts +2 -3
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/history/index.d.ts +1 -1
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +6 -3
- package/dist/history/index.mjs +5 -3
- package/dist/history/tursoStore.d.ts +6 -4
- package/dist/history/tursoStore.d.ts.map +1 -1
- package/dist/images/index.js +2 -2
- package/dist/images/index.mjs +2 -2
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +596 -295
- package/dist/index.mjs +595 -295
- package/dist/mcp-server/index.d.ts +5 -5
- package/dist/mcp-server/index.d.ts.map +1 -1
- package/dist/mcp-server/logger.d.ts +12 -2
- package/dist/mcp-server/logger.d.ts.map +1 -1
- package/dist/mcp-server/rate-limiter.d.ts +10 -3
- package/dist/mcp-server/rate-limiter.d.ts.map +1 -1
- package/dist/mcp-server/types.d.ts +20 -5
- package/dist/mcp-server/types.d.ts.map +1 -1
- package/dist/mcp-server.js +479 -244
- package/dist/mcp-server.mjs +485 -244
- package/dist/middleware/apiKey.d.ts +10 -0
- package/dist/middleware/apiKey.d.ts.map +1 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/cors.d.ts.map +1 -1
- package/dist/middleware/ratelimit.d.ts.map +1 -1
- package/dist/middleware/referrer.d.ts.map +1 -1
- package/dist/middleware/session.d.ts +1 -1
- package/dist/middleware/session.d.ts.map +1 -1
- package/dist/routes/demo.d.ts.map +1 -1
- package/dist/routes/openai.d.ts.map +1 -1
- package/dist/routes/private.d.ts.map +1 -1
- package/dist/routes/public.d.ts.map +1 -1
- package/dist/scheduler/index.js +1 -1
- package/dist/scheduler/index.mjs +1 -1
- package/dist/scheduler/scheduler.d.ts +9 -1
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +491 -268
- package/dist/server.mjs +497 -268
- package/dist/shortener.d.ts +5 -4
- package/dist/shortener.d.ts.map +1 -1
- package/dist/types.d.ts +129 -75
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -1
|
@@ -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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
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
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
719
|
-
|
|
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.
|
|
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
|
|
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
|
|
990
|
+
let initialDirect;
|
|
846
991
|
try {
|
|
847
992
|
const direct = await api.getAccountData(me.userId, "m.direct");
|
|
848
|
-
if (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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
*
|
|
54
|
-
* messages
|
|
55
|
-
*
|
|
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),
|
|
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;
|
|
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 {
|
|
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:
|
|
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":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}
|