@poncho-ai/messaging 0.7.6 → 0.7.7
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +36 -1
- package/package.json +1 -1
- package/src/adapters/telegram/index.ts +44 -0
- package/src/bridge.ts +8 -0
- package/src/types.ts +14 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/messaging@0.7.
|
|
2
|
+
> @poncho-ai/messaging@0.7.7 build /home/runner/work/poncho-ai/poncho-ai/packages/messaging
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m52.41 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 89ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m11.
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 5653ms
|
|
14
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m11.72 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @poncho-ai/messaging
|
|
2
2
|
|
|
3
|
+
## 0.7.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#61](https://github.com/cesr/poncho-ai/pull/61) [`0a51abe`](https://github.com/cesr/poncho-ai/commit/0a51abec12191397fd36ab1fd4feca7460489e33) Thanks [@cesr](https://github.com/cesr)! - Fix /new command on Telegram in serverless environments: persist conversation reset to the store so it survives cold starts.
|
|
8
|
+
|
|
3
9
|
## 0.7.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ interface IncomingMessage {
|
|
|
26
26
|
raw: unknown;
|
|
27
27
|
}
|
|
28
28
|
type IncomingMessageHandler = (message: IncomingMessage) => Promise<void>;
|
|
29
|
+
type ResetHandler = (platform: string, threadRef: ThreadRef) => Promise<void>;
|
|
29
30
|
type RouteHandler = (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
|
30
31
|
type RouteRegistrar = (method: "GET" | "POST", path: string, handler: RouteHandler) => void;
|
|
31
32
|
interface MessagingAdapter {
|
|
@@ -43,6 +44,8 @@ interface MessagingAdapter {
|
|
|
43
44
|
initialize(): Promise<void>;
|
|
44
45
|
/** Set the handler that processes incoming messages. */
|
|
45
46
|
onMessage(handler: IncomingMessageHandler): void;
|
|
47
|
+
/** Set the handler called when a user resets the conversation (e.g. /new). */
|
|
48
|
+
onReset?(handler: ResetHandler): void;
|
|
46
49
|
/** Post a reply back to the originating thread. */
|
|
47
50
|
sendReply(threadRef: ThreadRef, content: string, options?: {
|
|
48
51
|
files?: FileAttachment[];
|
|
@@ -89,6 +92,11 @@ interface AgentRunner {
|
|
|
89
92
|
steps?: number;
|
|
90
93
|
maxSteps?: number;
|
|
91
94
|
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Reset a conversation by clearing its messages, making the next
|
|
97
|
+
* interaction start fresh while keeping the same conversation ID.
|
|
98
|
+
*/
|
|
99
|
+
resetConversation?(conversationId: string): Promise<void>;
|
|
92
100
|
}
|
|
93
101
|
interface AgentBridgeOptions {
|
|
94
102
|
adapter: MessagingAdapter;
|
|
@@ -223,6 +231,7 @@ declare class TelegramAdapter implements MessagingAdapter {
|
|
|
223
231
|
private readonly webhookSecretEnv;
|
|
224
232
|
private readonly allowedUserIds;
|
|
225
233
|
private handler;
|
|
234
|
+
private resetHandler;
|
|
226
235
|
private approvalDecisionHandler;
|
|
227
236
|
private readonly sessionCounters;
|
|
228
237
|
private readonly approvalMessageIds;
|
|
@@ -230,6 +239,7 @@ declare class TelegramAdapter implements MessagingAdapter {
|
|
|
230
239
|
constructor(options?: TelegramAdapterOptions);
|
|
231
240
|
initialize(): Promise<void>;
|
|
232
241
|
onMessage(handler: IncomingMessageHandler): void;
|
|
242
|
+
onReset(handler: ResetHandler): void;
|
|
233
243
|
registerRoutes(router: RouteRegistrar): void;
|
|
234
244
|
sendReply(threadRef: ThreadRef, content: string, options?: {
|
|
235
245
|
files?: FileAttachment[];
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,12 @@ var AgentBridge = class {
|
|
|
30
30
|
this.waitUntil(processing);
|
|
31
31
|
return processing;
|
|
32
32
|
});
|
|
33
|
+
if (this.adapter.onReset && this.runner.resetConversation) {
|
|
34
|
+
this.adapter.onReset(async (platform, threadRef) => {
|
|
35
|
+
const conversationId = conversationIdFromThread(platform, threadRef);
|
|
36
|
+
await this.runner.resetConversation(conversationId);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
33
39
|
await this.adapter.initialize();
|
|
34
40
|
}
|
|
35
41
|
async handleMessage(message) {
|
|
@@ -1186,6 +1192,12 @@ var stripMention2 = (text, entities, botUsername, botId) => {
|
|
|
1186
1192
|
// src/adapters/telegram/index.ts
|
|
1187
1193
|
var TYPING_INTERVAL_MS = 4e3;
|
|
1188
1194
|
var NEW_COMMAND_RE = /^\/new(?:@(\S+))?$/i;
|
|
1195
|
+
var parseMessageThreadId = (platformThreadId, chatId) => {
|
|
1196
|
+
const parts = platformThreadId.split(":");
|
|
1197
|
+
if (parts.length !== 3 || parts[0] !== chatId) return void 0;
|
|
1198
|
+
const threadId = Number(parts[1]);
|
|
1199
|
+
return Number.isInteger(threadId) ? threadId : void 0;
|
|
1200
|
+
};
|
|
1189
1201
|
var collectBody3 = (req) => new Promise((resolve, reject) => {
|
|
1190
1202
|
const chunks = [];
|
|
1191
1203
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -1204,6 +1216,7 @@ var TelegramAdapter = class {
|
|
|
1204
1216
|
webhookSecretEnv;
|
|
1205
1217
|
allowedUserIds;
|
|
1206
1218
|
handler;
|
|
1219
|
+
resetHandler;
|
|
1207
1220
|
approvalDecisionHandler;
|
|
1208
1221
|
sessionCounters = /* @__PURE__ */ new Map();
|
|
1209
1222
|
approvalMessageIds = /* @__PURE__ */ new Map();
|
|
@@ -1231,6 +1244,9 @@ var TelegramAdapter = class {
|
|
|
1231
1244
|
onMessage(handler) {
|
|
1232
1245
|
this.handler = handler;
|
|
1233
1246
|
}
|
|
1247
|
+
onReset(handler) {
|
|
1248
|
+
this.resetHandler = handler;
|
|
1249
|
+
}
|
|
1234
1250
|
registerRoutes(router) {
|
|
1235
1251
|
router(
|
|
1236
1252
|
"POST",
|
|
@@ -1241,11 +1257,16 @@ var TelegramAdapter = class {
|
|
|
1241
1257
|
async sendReply(threadRef, content, options) {
|
|
1242
1258
|
const chatId = threadRef.channelId;
|
|
1243
1259
|
const replyTo = threadRef.messageId ? Number(threadRef.messageId) : void 0;
|
|
1260
|
+
const messageThreadId = parseMessageThreadId(
|
|
1261
|
+
threadRef.platformThreadId,
|
|
1262
|
+
chatId
|
|
1263
|
+
);
|
|
1244
1264
|
if (content) {
|
|
1245
1265
|
const chunks = splitMessage2(content);
|
|
1246
1266
|
for (const chunk of chunks) {
|
|
1247
1267
|
await sendMessage(this.botToken, chatId, chunk, {
|
|
1248
|
-
reply_to_message_id: replyTo
|
|
1268
|
+
reply_to_message_id: replyTo,
|
|
1269
|
+
message_thread_id: messageThreadId
|
|
1249
1270
|
});
|
|
1250
1271
|
}
|
|
1251
1272
|
}
|
|
@@ -1254,11 +1275,13 @@ var TelegramAdapter = class {
|
|
|
1254
1275
|
if (file.mediaType.startsWith("image/")) {
|
|
1255
1276
|
await sendPhoto(this.botToken, chatId, file.data, {
|
|
1256
1277
|
reply_to_message_id: replyTo,
|
|
1278
|
+
message_thread_id: messageThreadId,
|
|
1257
1279
|
filename: file.filename
|
|
1258
1280
|
});
|
|
1259
1281
|
} else {
|
|
1260
1282
|
await sendDocument(this.botToken, chatId, file.data, {
|
|
1261
1283
|
reply_to_message_id: replyTo,
|
|
1284
|
+
message_thread_id: messageThreadId,
|
|
1262
1285
|
filename: file.filename,
|
|
1263
1286
|
mediaType: file.mediaType
|
|
1264
1287
|
});
|
|
@@ -1418,6 +1441,18 @@ ${inputSummary}` : "(no input)"
|
|
|
1418
1441
|
this.sessionCounters.set(key2, current + 1);
|
|
1419
1442
|
res.writeHead(200);
|
|
1420
1443
|
res.end();
|
|
1444
|
+
if (this.resetHandler) {
|
|
1445
|
+
const topicId2 = message.message_thread_id;
|
|
1446
|
+
const prevThreadId = topicId2 ? `${chatId}:${topicId2}:${current}` : `${chatId}:${current}`;
|
|
1447
|
+
try {
|
|
1448
|
+
await this.resetHandler("telegram", {
|
|
1449
|
+
channelId: chatId,
|
|
1450
|
+
platformThreadId: prevThreadId
|
|
1451
|
+
});
|
|
1452
|
+
} catch (err) {
|
|
1453
|
+
console.error("[telegram-adapter] reset handler error:", err instanceof Error ? err.message : err);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1421
1456
|
await sendMessage(
|
|
1422
1457
|
this.botToken,
|
|
1423
1458
|
chatId,
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
IncomingMessage as PonchoIncomingMessage,
|
|
5
5
|
IncomingMessageHandler,
|
|
6
6
|
MessagingAdapter,
|
|
7
|
+
ResetHandler,
|
|
7
8
|
RouteRegistrar,
|
|
8
9
|
ThreadRef,
|
|
9
10
|
} from "../../types.js";
|
|
@@ -30,6 +31,19 @@ import {
|
|
|
30
31
|
const TYPING_INTERVAL_MS = 4_000;
|
|
31
32
|
const NEW_COMMAND_RE = /^\/new(?:@(\S+))?$/i;
|
|
32
33
|
|
|
34
|
+
const parseMessageThreadId = (
|
|
35
|
+
platformThreadId: string,
|
|
36
|
+
chatId: string,
|
|
37
|
+
): number | undefined => {
|
|
38
|
+
const parts = platformThreadId.split(":");
|
|
39
|
+
// Telegram thread format:
|
|
40
|
+
// - non-topic chats: `${chatId}:${session}`
|
|
41
|
+
// - forum topics: `${chatId}:${message_thread_id}:${session}`
|
|
42
|
+
if (parts.length !== 3 || parts[0] !== chatId) return undefined;
|
|
43
|
+
const threadId = Number(parts[1]);
|
|
44
|
+
return Number.isInteger(threadId) ? threadId : undefined;
|
|
45
|
+
};
|
|
46
|
+
|
|
33
47
|
export interface TelegramApprovalInfo {
|
|
34
48
|
approvalId: string;
|
|
35
49
|
tool: string;
|
|
@@ -69,6 +83,7 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
69
83
|
private readonly webhookSecretEnv: string;
|
|
70
84
|
private readonly allowedUserIds: number[] | undefined;
|
|
71
85
|
private handler: IncomingMessageHandler | undefined;
|
|
86
|
+
private resetHandler: ResetHandler | undefined;
|
|
72
87
|
private approvalDecisionHandler: TelegramApprovalDecisionHandler | undefined;
|
|
73
88
|
private readonly sessionCounters = new Map<string, number>();
|
|
74
89
|
private readonly approvalMessageIds = new Map<string, { chatId: string; messageId: number }>();
|
|
@@ -107,6 +122,10 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
107
122
|
this.handler = handler;
|
|
108
123
|
}
|
|
109
124
|
|
|
125
|
+
onReset(handler: ResetHandler): void {
|
|
126
|
+
this.resetHandler = handler;
|
|
127
|
+
}
|
|
128
|
+
|
|
110
129
|
registerRoutes(router: RouteRegistrar): void {
|
|
111
130
|
router("POST", "/api/messaging/telegram", (req, res) =>
|
|
112
131
|
this.handleRequest(req, res),
|
|
@@ -122,12 +141,17 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
122
141
|
const replyTo = threadRef.messageId
|
|
123
142
|
? Number(threadRef.messageId)
|
|
124
143
|
: undefined;
|
|
144
|
+
const messageThreadId = parseMessageThreadId(
|
|
145
|
+
threadRef.platformThreadId,
|
|
146
|
+
chatId,
|
|
147
|
+
);
|
|
125
148
|
|
|
126
149
|
if (content) {
|
|
127
150
|
const chunks = splitMessage(content);
|
|
128
151
|
for (const chunk of chunks) {
|
|
129
152
|
await sendMessage(this.botToken, chatId, chunk, {
|
|
130
153
|
reply_to_message_id: replyTo,
|
|
154
|
+
message_thread_id: messageThreadId,
|
|
131
155
|
});
|
|
132
156
|
}
|
|
133
157
|
}
|
|
@@ -137,11 +161,13 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
137
161
|
if (file.mediaType.startsWith("image/")) {
|
|
138
162
|
await sendPhoto(this.botToken, chatId, file.data, {
|
|
139
163
|
reply_to_message_id: replyTo,
|
|
164
|
+
message_thread_id: messageThreadId,
|
|
140
165
|
filename: file.filename,
|
|
141
166
|
});
|
|
142
167
|
} else {
|
|
143
168
|
await sendDocument(this.botToken, chatId, file.data, {
|
|
144
169
|
reply_to_message_id: replyTo,
|
|
170
|
+
message_thread_id: messageThreadId,
|
|
145
171
|
filename: file.filename,
|
|
146
172
|
mediaType: file.mediaType,
|
|
147
173
|
});
|
|
@@ -355,6 +381,24 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
355
381
|
res.writeHead(200);
|
|
356
382
|
res.end();
|
|
357
383
|
|
|
384
|
+
// Persist the reset so it survives serverless cold starts.
|
|
385
|
+
// The in-memory counter handles long-running processes; the bridge
|
|
386
|
+
// clears messages in the conversation store for serverless.
|
|
387
|
+
if (this.resetHandler) {
|
|
388
|
+
const topicId = message.message_thread_id;
|
|
389
|
+
const prevThreadId = topicId
|
|
390
|
+
? `${chatId}:${topicId}:${current}`
|
|
391
|
+
: `${chatId}:${current}`;
|
|
392
|
+
try {
|
|
393
|
+
await this.resetHandler("telegram", {
|
|
394
|
+
channelId: chatId,
|
|
395
|
+
platformThreadId: prevThreadId,
|
|
396
|
+
});
|
|
397
|
+
} catch (err) {
|
|
398
|
+
console.error("[telegram-adapter] reset handler error:", err instanceof Error ? err.message : err);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
358
402
|
await sendMessage(
|
|
359
403
|
this.botToken,
|
|
360
404
|
chatId,
|
package/src/bridge.ts
CHANGED
|
@@ -50,6 +50,14 @@ export class AgentBridge {
|
|
|
50
50
|
this.waitUntil(processing);
|
|
51
51
|
return processing;
|
|
52
52
|
});
|
|
53
|
+
|
|
54
|
+
if (this.adapter.onReset && this.runner.resetConversation) {
|
|
55
|
+
this.adapter.onReset(async (platform, threadRef) => {
|
|
56
|
+
const conversationId = conversationIdFromThread(platform, threadRef);
|
|
57
|
+
await this.runner.resetConversation!(conversationId);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
53
61
|
await this.adapter.initialize();
|
|
54
62
|
}
|
|
55
63
|
|
package/src/types.ts
CHANGED
|
@@ -33,6 +33,11 @@ export type IncomingMessageHandler = (
|
|
|
33
33
|
message: IncomingMessage,
|
|
34
34
|
) => Promise<void>;
|
|
35
35
|
|
|
36
|
+
export type ResetHandler = (
|
|
37
|
+
platform: string,
|
|
38
|
+
threadRef: ThreadRef,
|
|
39
|
+
) => Promise<void>;
|
|
40
|
+
|
|
36
41
|
// ---------------------------------------------------------------------------
|
|
37
42
|
// Route registration (adapter ↔ HTTP server contract)
|
|
38
43
|
// ---------------------------------------------------------------------------
|
|
@@ -73,6 +78,9 @@ export interface MessagingAdapter {
|
|
|
73
78
|
/** Set the handler that processes incoming messages. */
|
|
74
79
|
onMessage(handler: IncomingMessageHandler): void;
|
|
75
80
|
|
|
81
|
+
/** Set the handler called when a user resets the conversation (e.g. /new). */
|
|
82
|
+
onReset?(handler: ResetHandler): void;
|
|
83
|
+
|
|
76
84
|
/** Post a reply back to the originating thread. */
|
|
77
85
|
sendReply(
|
|
78
86
|
threadRef: ThreadRef,
|
|
@@ -133,6 +141,12 @@ export interface AgentRunner {
|
|
|
133
141
|
steps?: number;
|
|
134
142
|
maxSteps?: number;
|
|
135
143
|
}>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Reset a conversation by clearing its messages, making the next
|
|
147
|
+
* interaction start fresh while keeping the same conversation ID.
|
|
148
|
+
*/
|
|
149
|
+
resetConversation?(conversationId: string): Promise<void>;
|
|
136
150
|
}
|
|
137
151
|
|
|
138
152
|
// ---------------------------------------------------------------------------
|