@diegoaltoworks/chatter 0.60.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.
Files changed (125) hide show
  1. package/README.md +56 -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 +152 -0
  15. package/dist/channels/matrix/api.d.ts.map +1 -0
  16. package/dist/channels/matrix/channel.d.ts +46 -0
  17. package/dist/channels/matrix/channel.d.ts.map +1 -0
  18. package/dist/channels/matrix/handler.d.ts +58 -0
  19. package/dist/channels/matrix/handler.d.ts.map +1 -0
  20. package/dist/channels/matrix/index.d.ts +37 -0
  21. package/dist/channels/matrix/index.d.ts.map +1 -0
  22. package/dist/channels/matrix/index.js +1128 -0
  23. package/dist/channels/matrix/index.mjs +1079 -0
  24. package/dist/channels/matrix/sync.d.ts +53 -0
  25. package/dist/channels/matrix/sync.d.ts.map +1 -0
  26. package/dist/channels/matrix/updates.d.ts +104 -0
  27. package/dist/channels/matrix/updates.d.ts.map +1 -0
  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 +25 -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 +123 -41
  49. package/dist/channels/whatsapp/index.mjs +120 -41
  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 +11 -2
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The `/sync` long-poll loop, isolated from both the HTTP client and the
3
+ * channel so its failure behaviour is testable without either — the same
4
+ * shape as `../telegram/poll.ts`'s `runLongPoll`, adapted to Matrix's
5
+ * `since`-token pagination instead of an acknowledged offset.
6
+ *
7
+ * The property that matters: a homeserver that is down, rate-limiting, or
8
+ * rejecting the token must never turn into a tight retry loop. Every
9
+ * failure backs off exponentially, and `M_LIMIT_EXCEEDED`'s own
10
+ * `retry_after_ms` wins over that backoff when the homeserver sends one.
11
+ */
12
+ import { type Logger } from "../../core/logger";
13
+ import { type MatrixSyncResponse } from "./api";
14
+ /** Exponential backoff: 2s, 4s, 8s ... capped at a minute. `failures` is the count including the one just seen. */
15
+ export declare function syncBackoffMs(failures: number): number;
16
+ /**
17
+ * How long to wait after a failed sync. `M_LIMIT_EXCEEDED`'s
18
+ * `retry_after_ms` is an instruction, not a hint — honour it verbatim even
19
+ * when it exceeds the backoff cap, because syncing again sooner just
20
+ * extends the rate-limit window.
21
+ */
22
+ export declare function retryDelayMs(error: unknown, failures: number): number;
23
+ export interface MatrixSyncLoopDeps {
24
+ /** One `/sync` call. Rejections are backed off; they never end the loop. */
25
+ sync(since: string | undefined): Promise<MatrixSyncResponse>;
26
+ /** Per-batch work. A throw is logged and skipped — one bad batch never stops the loop. */
27
+ handleSync(response: MatrixSyncResponse): Promise<void>;
28
+ /** Checked before every sync, and again after a failure, so `stop()` cuts the loop instead of sleeping out a backoff first. */
29
+ isStopped(): boolean;
30
+ /** Overridable for tests; a real channel passes a `setTimeout`-based sleep. */
31
+ sleep(ms: number): Promise<void>;
32
+ /** Where this loop's `since` token resumes from. Omitted = an initial full sync. */
33
+ initialSince?: string;
34
+ /** Called with each newly acknowledged `since` token, for a host that persists it. */
35
+ onSince?(since: string): void;
36
+ logger?: Logger;
37
+ /** Prefix for log lines, e.g. `Matrix[@bot:example.org]`. */
38
+ label?: string;
39
+ }
40
+ /**
41
+ * Syncs until `isStopped()`, resolving with the last `since` token reached
42
+ * (what a restart would resume from).
43
+ *
44
+ * The token advances BEFORE the batch is handled, matching the Telegram
45
+ * poll loop's offset-first ordering: a handler that throws on one bad
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. 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.
51
+ */
52
+ export declare function runMatrixSyncLoop(deps: MatrixSyncLoopDeps): Promise<string | undefined>;
53
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +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;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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Matrix room event -> {@link ChannelMessage}: the transport's own half of
3
+ * the channel contract (see docs/build-a-channel.md), kept pure so mention,
4
+ * reply-to and DM detection are testable without a network or a running
5
+ * channel.
6
+ *
7
+ * Addressing parity with the other built-in channels is the point of this
8
+ * module. WhatsApp and Telegram both resolve "was the bot addressed?" from
9
+ * transport-native structures; Matrix resolves it from the stable
10
+ * `m.mentions` field (MSC3952, in the spec since v1.7) with a pill-in-HTML
11
+ * fallback for clients that only set that. The gate policy that consumes
12
+ * those booleans (`decideChannelAction`) is identical for every channel.
13
+ */
14
+ import type { ChannelMessage } from "../gates";
15
+ import type { MatrixAccountDataEvent, MatrixEvent, MatrixInvitedRoom } from "./api";
16
+ /** The bot's own identity, as `whoami` reports it. */
17
+ export interface MatrixIdentity {
18
+ userId: string;
19
+ }
20
+ interface MatrixMentions {
21
+ user_ids?: string[];
22
+ room?: boolean;
23
+ }
24
+ interface MatrixInReplyTo {
25
+ event_id?: string;
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
+ }
33
+ interface MatrixMessageContent {
34
+ msgtype?: string;
35
+ body?: string;
36
+ format?: string;
37
+ formatted_body?: string;
38
+ "m.mentions"?: MatrixMentions;
39
+ "m.relates_to"?: MatrixRelatesTo;
40
+ }
41
+ /** The message text, or "" for an event this channel doesn't render as text (a redaction, a non-text msgtype with no body). */
42
+ export declare function messageText(content: MatrixMessageContent): string;
43
+ export declare function mentionsBot(content: MatrixMessageContent, me: MatrixIdentity): boolean;
44
+ /**
45
+ * Was this message a reply to one the bot sent? `sentEventIds` is the
46
+ * session's own record of event ids it has sent (see `./channel`) — Matrix
47
+ * has no cheap "who sent event X" lookup without fetching that event, and a
48
+ * bot only ever needs to recognise its OWN messages, which it already knows
49
+ * without asking the server.
50
+ */
51
+ export declare function isReplyToBot(content: MatrixMessageContent, sentEventIds: ReadonlySet<string>): boolean;
52
+ /** How many of the session's own sent event ids {@link isReplyToBot} remembers — bounded so a long-running bot's memory doesn't grow with every message it ever sent. */
53
+ export declare const MAX_TRACKED_SENT_EVENTS = 500;
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). */
55
+ export declare function recordSentEventId(sentEventIds: Set<string>, eventId: string): void;
56
+ /**
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.
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. */
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;
88
+ /**
89
+ * The {@link ChannelMessage} for a timeline event, or `undefined` when
90
+ * there is nothing for a chat pipeline to look at: not an `m.room.message`
91
+ * (a reaction, a membership change, an encrypted event this channel can't
92
+ * decrypt), one with no `msgtype` at all (a redacted message), or an edit of
93
+ * a message that was already delivered (see below).
94
+ */
95
+ export declare function toChannelMessage(roomId: string, event: MatrixEvent, me: MatrixIdentity, directRooms: ReadonlySet<string>, sentEventIds: ReadonlySet<string>): ChannelMessage | undefined;
96
+ /**
97
+ * A stable identity key for `bucketsFor`/`personaResolver`/`answerFn`.
98
+ * Namespaced so a bare Matrix user id (which is itself already `@user:
99
+ * server.tld`, unlike Telegram's bare numeral) never collides with another
100
+ * channel's key when a host shares persona/spend state across channels.
101
+ */
102
+ export declare function matrixSenderKey(userId: string): string;
103
+ export {};
104
+ //# sourceMappingURL=updates.d.ts.map
@@ -0,0 +1 @@
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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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared config surface for a long-poll style channel (Telegram, Matrix): the
3
+ * gates/persona/history/rate-limit knobs `createInboundPipeline` accepts, the
4
+ * brain hooks (via `BrainHooks`), and the sleep/now/logger overrides tests
5
+ * use. Each transport extends this with its own protocol specifics (poll
6
+ * timeout vs. sync timeout, bot token vs. homeserver credentials, ...): see
7
+ * `./telegram/channel.ts` and `./matrix/channel.ts`.
8
+ */
9
+ import type { Logger } from "../core/logger";
10
+ import type { HistoryCompactionOptions } from "../history/compaction";
11
+ import type { HistoryStore } from "../history/types";
12
+ import type { BrainHooks } from "../types";
13
+ export interface PollingChannelConfig extends BrainHooks {
14
+ /** Channel and sender-registry name. Override to run more than one bot in one process. */
15
+ name?: string;
16
+ /** Group chats eligible for a reply. Empty (default) = every group/room. Has no effect on DMs, which always reply. */
17
+ allowedChats?: string[];
18
+ model?: string;
19
+ /** Extra system-prompt section describing the delivery channel; passed through to `prepareChat`. */
20
+ channelHint?: string;
21
+ /** A throw/rejection is treated as "no persona" for that turn. */
22
+ personaResolver?: (ctx: {
23
+ sender: string;
24
+ text: string;
25
+ }) => string | undefined | Promise<string | undefined>;
26
+ /** Off by default — the channel stays single-turn until a store is configured. */
27
+ history?: {
28
+ store: HistoryStore;
29
+ /** Most recent turns to load per reply. @default 20 */
30
+ limit?: number;
31
+ /**
32
+ * Excludes a sender from history entirely — see
33
+ * `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
34
+ * @default every sender is enabled
35
+ */
36
+ historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
37
+ /**
38
+ * Summarize-then-truncate compaction — see
39
+ * `InboundPipelineConfig.history.compaction` in `./channels`.
40
+ * @default off
41
+ */
42
+ compaction?: HistoryCompactionOptions;
43
+ };
44
+ muteRegex?: RegExp;
45
+ unmuteRegex?: RegExp;
46
+ /** Neutral, overridable acknowledgements — this module ships no bot personality; unset = silent mute/unmute. */
47
+ muteReply?: string;
48
+ unmuteReply?: string;
49
+ dmRateLimit?: {
50
+ max: number;
51
+ windowMs: number;
52
+ };
53
+ groupRateLimit?: {
54
+ max: number;
55
+ windowMs: number;
56
+ };
57
+ /** Overridable for tests and for hosts routing through a proxy; defaults to `globalThis.fetch`. */
58
+ fetch?: typeof fetch;
59
+ /** Overridable for tests; defaults to a `setTimeout`-based sleep. */
60
+ sleep?: (ms: number) => Promise<void>;
61
+ now?: () => number;
62
+ /** Logger for poll/gate diagnostics. Falls back to the host's `deps.logger`, then a console logger. */
63
+ logger?: Logger;
64
+ }
65
+ /** `setTimeout`-based sleep, `unref`'d where supported so a pending sleep never keeps the process alive on its own. */
66
+ export declare function defaultSleep(ms: number): Promise<void>;
67
+ //# sourceMappingURL=polling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling.d.ts","sourceRoot":"","sources":["../../src/channels/polling.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,oBAAqB,SAAQ,UAAU;IACtD,0FAA0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sHAAsH;IACtH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,YAAY,CAAC;QACpB,uDAAuD;QACvD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE;;;;WAIG;QACH,UAAU,CAAC,EAAE,wBAAwB,CAAC;KACvC,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uHAAuH;AACvH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtD"}
@@ -20,30 +20,14 @@
20
20
  * Telegram's privacy mode means it only receives messages that address it —
21
21
  * which is the same policy `decideChannelAction` applies anyway.
22
22
  */
23
- import type { AnswerFn, TransformReply } from "../../core/answer";
24
- import type { BucketsFor } from "../../core/buckets";
25
- import { type Logger } from "../../core/logger";
26
- import type { RerankContext, RewriteQuery } from "../../core/pipeline";
27
- import type { HistoryCompactionOptions } from "../../history/compaction";
28
- import type { HistoryStore } from "../../history/types";
29
23
  import type { Channel } from "../index";
24
+ import { type PollingChannelConfig } from "../polling";
30
25
  import { type TelegramApi } from "./api";
31
- export interface TelegramChannelConfig {
26
+ export interface TelegramChannelConfig extends PollingChannelConfig {
32
27
  /** From @BotFather. A credential — pass it from the environment, never commit it. */
33
28
  botToken: string;
34
29
  /** Channel and sender-registry name. Override to run more than one bot in one process. @default "telegram" */
35
30
  name?: string;
36
- /** Group chats eligible for a reply. Empty (default) = every group. Has no effect on DMs, which always reply. */
37
- allowedChats?: string[];
38
- answerFn?: AnswerFn;
39
- bucketsFor?: BucketsFor;
40
- /** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. Falls back to the server's own. */
41
- rewriteQuery?: RewriteQuery;
42
- /** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. Falls back to the server's own. */
43
- rerankContext?: RerankContext;
44
- /** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. Falls back to the server's own. */
45
- transformReply?: TransformReply;
46
- model?: string;
47
31
  /** Extra system-prompt section describing the delivery channel; passed through to `prepareChat`. @default "Channel: Telegram." */
48
32
  channelHint?: string;
49
33
  /** A throw/rejection is treated as "no persona" for that turn. `sender` is the namespaced `tg:<id>` key. */
@@ -51,54 +35,16 @@ export interface TelegramChannelConfig {
51
35
  sender: string;
52
36
  text: string;
53
37
  }) => string | undefined | Promise<string | undefined>;
54
- /** Off by default — the channel stays single-turn until a store is configured. */
55
- history?: {
56
- store: HistoryStore;
57
- /** Most recent turns to load per reply. @default 20 */
58
- limit?: number;
59
- /**
60
- * Excludes a sender from history entirely — see
61
- * `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
62
- * @default every sender is enabled
63
- */
64
- historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
65
- /**
66
- * Summarize-then-truncate compaction — see
67
- * `InboundPipelineConfig.history.compaction` in `./channels`.
68
- * @default off
69
- */
70
- compaction?: HistoryCompactionOptions;
71
- };
72
- muteRegex?: RegExp;
73
- unmuteRegex?: RegExp;
74
- /** Neutral, overridable acknowledgements — this module ships no bot personality; unset = silent mute/unmute. */
75
- muteReply?: string;
76
- unmuteReply?: string;
77
- dmRateLimit?: {
78
- max: number;
79
- windowMs: number;
80
- };
81
- groupRateLimit?: {
82
- max: number;
83
- windowMs: number;
84
- };
85
38
  /** @default 30 */
86
39
  pollTimeoutSeconds?: number;
87
40
  /** Resume point for `getUpdates`. Omitted = whatever Telegram still has queued (see docs/telegram.md). */
88
41
  initialOffset?: number;
89
42
  /** Called with each acknowledged offset, for a host that wants to persist it across restarts. */
90
43
  onOffset?: (offset: number) => void;
91
- /** Overridable for tests and for hosts routing through a proxy; defaults to `globalThis.fetch`. */
92
- fetch?: typeof fetch;
93
44
  /** Self-hosted Bot API server origin. */
94
45
  apiBaseUrl?: string;
95
46
  /** Overridable for tests, which fake the Bot API instead of calling it; defaults to a `fetch` client over {@link TelegramChannelConfig.botToken}. */
96
47
  api?: TelegramApi;
97
- /** Overridable for tests; defaults to a `setTimeout`-based sleep. */
98
- sleep?: (ms: number) => Promise<void>;
99
- now?: () => number;
100
- /** Logger for poll/gate diagnostics. Falls back to the host's `deps.logger`, then a console logger. */
101
- logger?: Logger;
102
48
  }
103
49
  export declare function createTelegramChannel(config: TelegramChannelConfig): Channel;
104
50
  //# sourceMappingURL=channel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAQ5D,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kIAAkI;IAClI,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,YAAY,CAAC;QACpB,uDAAuD;QACvD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACnE;;;;WAIG;QACH,UAAU,CAAC,EAAE,wBAAwB,CAAC;KACvC,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAuG5E"}
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAQ5D,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAmG5E"}
@@ -294,7 +294,8 @@ async function answerOnce({
294
294
  async function applyTransformReply(transformReply, input, logger) {
295
295
  if (!transformReply) return input.text;
296
296
  try {
297
- return await transformReply(input);
297
+ const result = await transformReply(input);
298
+ return typeof result === "string" ? result : null;
298
299
  } catch (error) {
299
300
  logger?.error(
300
301
  `transformReply threw for channel "${input.channel}"; sending the original reply`,
@@ -356,6 +357,7 @@ async function prepareChat({
356
357
  sender,
357
358
  rewriteQuery,
358
359
  rerankContext,
360
+ fallbackFn,
359
361
  logger
360
362
  }) {
361
363
  const lastUserMsg = lastUserMessage(messages);
@@ -372,21 +374,39 @@ async function prepareChat({
372
374
  logger?.error("rewriteQuery threw; falling back to the original query", error);
373
375
  }
374
376
  }
375
- let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
377
+ const resolvedBuckets = buckets ?? defaultBuckets(mode);
378
+ const retrievedChunks = await store.query(query, topK, resolvedBuckets);
379
+ let ctx = retrievedChunks;
376
380
  if (rerankContext) {
377
381
  try {
378
- const reranked = await rerankContext({ query, chunks: ctx });
382
+ const reranked = await rerankContext({ query, chunks: [...ctx] });
379
383
  if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
380
384
  } catch (error) {
381
385
  logger?.error("rerankContext threw; falling back to the original chunks", error);
382
386
  }
383
387
  }
388
+ let fallback;
389
+ if (ctx.length === 0 && fallbackFn) {
390
+ try {
391
+ const guidance = await fallbackFn({
392
+ query,
393
+ mode,
394
+ buckets: resolvedBuckets,
395
+ sender,
396
+ retrievedChunks
397
+ });
398
+ if (typeof guidance === "string" && guidance.trim()) fallback = guidance.trim();
399
+ } catch (error) {
400
+ logger?.error("fallbackFn threw; proceeding without fallback guidance", error);
401
+ }
402
+ }
384
403
  const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
385
404
  const hint = channelHint?.trim();
386
405
  const system = [
387
406
  prompts.baseSystemRules,
388
407
  persona,
389
408
  ...hint ? [hint] : [],
409
+ ...fallback ? [fallback] : [],
390
410
  `${label}:
391
411
  ${ctx.join("\n\n")}`
392
412
  ].join("\n\n");
@@ -394,7 +414,7 @@ ${ctx.join("\n\n")}`
394
414
  }
395
415
 
396
416
  // src/history/compaction.ts
397
- var DEFAULT_TIMEOUT_MS = 8e3;
417
+ var COMPACTION_TIMEOUT_MS = 8e3;
398
418
  var LOAD_ALL = Number.MAX_SAFE_INTEGER;
399
419
  var SUMMARY_PREFIX = "Summary of earlier conversation: ";
400
420
  function defaultSummarize(client, model) {
@@ -422,7 +442,7 @@ function createHistoryCompactor(deps, options) {
422
442
  throw new Error(`Invalid history compaction keep: ${keep}`);
423
443
  }
424
444
  const summarize = options.summarize ?? defaultSummarize(deps.client, options.model);
425
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
445
+ const timeoutMs = options.timeoutMs ?? COMPACTION_TIMEOUT_MS;
426
446
  return {
427
447
  async maybeCompact(store, conversationId) {
428
448
  const turns = await store.load(conversationId, LOAD_ALL);
@@ -513,6 +533,16 @@ function underReplyRateLimit(msg, limiters) {
513
533
  }
514
534
 
515
535
  // src/channels/pipeline.ts
536
+ function resolveBrainHooks(config, fallback) {
537
+ return {
538
+ answerFn: config.answerFn ?? fallback?.answerFn,
539
+ bucketsFor: config.bucketsFor ?? fallback?.bucketsFor,
540
+ rewriteQuery: config.rewriteQuery ?? fallback?.rewriteQuery,
541
+ rerankContext: config.rerankContext ?? fallback?.rerankContext,
542
+ fallbackFn: config.fallbackFn ?? fallback?.fallbackFn,
543
+ transformReply: config.transformReply ?? fallback?.transformReply
544
+ };
545
+ }
516
546
  var DEFAULT_HISTORY_LIMIT = 20;
517
547
  var DEFAULT_GROUP_RATE_LIMIT = { max: 30, windowMs: 60 * 60 * 1e3 };
518
548
  var DEFAULT_DM_RATE_LIMIT = { max: 20, windowMs: 60 * 60 * 1e3 };
@@ -597,6 +627,7 @@ function createInboundPipeline(deps, config) {
597
627
  sender,
598
628
  rewriteQuery: config.rewriteQuery,
599
629
  rerankContext: config.rerankContext,
630
+ fallbackFn: config.fallbackFn,
600
631
  logger: config.logger
601
632
  });
602
633
  const { content: produced } = await answerOnce({
@@ -633,6 +664,14 @@ function createInboundPipeline(deps, config) {
633
664
  };
634
665
  }
635
666
 
667
+ // src/channels/polling.ts
668
+ function defaultSleep(ms) {
669
+ return new Promise((resolve) => {
670
+ const timer = setTimeout(resolve, ms);
671
+ if (typeof timer === "object" && "unref" in timer) timer.unref();
672
+ });
673
+ }
674
+
636
675
  // src/channels/telegram/updates.ts
637
676
  function messageText(message) {
638
677
  return message.text ?? message.caption ?? "";
@@ -711,11 +750,16 @@ function createTelegramSender(api) {
711
750
  };
712
751
  }
713
752
 
753
+ // src/channels/backoff.ts
754
+ function exponentialBackoffMs(baseMs, maxMs, n) {
755
+ return Math.min(baseMs * 2 ** Math.max(0, Math.min(n, 20)), maxMs);
756
+ }
757
+
714
758
  // src/channels/telegram/poll.ts
715
759
  var BASE_DELAY_MS = 1e3;
716
760
  var MAX_DELAY_MS = 6e4;
717
761
  function pollBackoffMs(failures) {
718
- return Math.min(BASE_DELAY_MS * 2 ** Math.min(failures, 20), MAX_DELAY_MS);
762
+ return exponentialBackoffMs(BASE_DELAY_MS, MAX_DELAY_MS, failures);
719
763
  }
720
764
  function retryDelayMs(error, failures) {
721
765
  const retryAfterMs = error instanceof TelegramApiError ? error.retryAfterMs : void 0;
@@ -740,9 +784,9 @@ async function runLongPoll(deps) {
740
784
  }
741
785
  failures = 0;
742
786
  for (const update of updates) {
787
+ if (deps.isStopped()) break;
743
788
  offset = nextOffset(update);
744
789
  deps.onOffset?.(offset);
745
- if (deps.isStopped()) break;
746
790
  try {
747
791
  await deps.handleUpdate(update);
748
792
  } catch (error) {
@@ -755,12 +799,6 @@ async function runLongPoll(deps) {
755
799
 
756
800
  // src/channels/telegram/channel.ts
757
801
  var DEFAULT_POLL_TIMEOUT_SECONDS = 30;
758
- function defaultSleep(ms) {
759
- return new Promise((resolve) => {
760
- const timer = setTimeout(resolve, ms);
761
- if (typeof timer === "object" && "unref" in timer) timer.unref();
762
- });
763
- }
764
802
  function createTelegramChannel(config) {
765
803
  const channelName = config.name ?? "telegram";
766
804
  const allowedChats = config.allowedChats ?? [];
@@ -788,11 +826,7 @@ function createTelegramChannel(config) {
788
826
  { client: deps.client, store: deps.store, prompts: deps.prompts },
789
827
  {
790
828
  channel: channelName,
791
- answerFn: config.answerFn ?? deps.config.answerFn,
792
- bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
793
- rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
794
- rerankContext: config.rerankContext ?? deps.config.rerankContext,
795
- transformReply: config.transformReply ?? deps.config.transformReply,
829
+ ...resolveBrainHooks(config, deps.config),
796
830
  model: config.model,
797
831
  channelHint: config.channelHint ?? "Channel: Telegram.",
798
832
  personaResolver: config.personaResolver,
@@ -971,11 +1005,7 @@ function createTelegramWebhookRoute(config) {
971
1005
  { client: deps.client, store: deps.store, prompts: deps.prompts },
972
1006
  {
973
1007
  channel: channelName,
974
- answerFn: config.answerFn ?? deps.config.answerFn,
975
- bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
976
- rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
977
- rerankContext: config.rerankContext ?? deps.config.rerankContext,
978
- transformReply: config.transformReply ?? deps.config.transformReply,
1008
+ ...resolveBrainHooks(config, deps.config),
979
1009
  model: config.model,
980
1010
  channelHint: config.channelHint ?? "Channel: Telegram.",
981
1011
  personaResolver: config.personaResolver,