@poncho-ai/messaging 0.3.0 → 0.5.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +50 -20
- package/package.json +1 -1
- package/src/adapters/telegram/index.ts +15 -0
- package/src/bridge.ts +53 -23
- package/src/types.ts +3 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/messaging@0.
|
|
2
|
+
> @poncho-ai/messaging@0.5.0 build /Users/cesar/Dev/latitude/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[32m45.94 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 60ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
14
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 2033ms
|
|
14
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m10.13 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @poncho-ai/messaging
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`8ef9316`](https://github.com/cesr/poncho-ai/commit/8ef93165084b4df581e39a1581b1ead64b7b3f42) Thanks [@cesr](https://github.com/cesr)! - Add auto-continuation support for messaging adapters (Telegram, Slack, Resend) on serverless platforms. When `PONCHO_MAX_DURATION` is set, agent runs that hit the soft deadline now automatically resume with "Continue" messages, matching the web UI and client SDK behavior.
|
|
8
|
+
|
|
9
|
+
## 0.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`3216e80`](https://github.com/cesr/poncho-ai/commit/3216e8072027896dd1cc5f29b1a7b0eea9ee1ff5) Thanks [@cesr](https://github.com/cesr)! - Add `allowedUserIds` option to Telegram adapter for restricting bot access to specific users.
|
|
14
|
+
|
|
3
15
|
## 0.3.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -83,6 +83,9 @@ interface AgentRunner {
|
|
|
83
83
|
}): Promise<{
|
|
84
84
|
response: string;
|
|
85
85
|
files?: FileAttachment[];
|
|
86
|
+
continuation?: boolean;
|
|
87
|
+
steps?: number;
|
|
88
|
+
maxSteps?: number;
|
|
86
89
|
}>;
|
|
87
90
|
}
|
|
88
91
|
interface AgentBridgeOptions {
|
|
@@ -192,6 +195,7 @@ declare class ResendAdapter implements MessagingAdapter {
|
|
|
192
195
|
interface TelegramAdapterOptions {
|
|
193
196
|
botTokenEnv?: string;
|
|
194
197
|
webhookSecretEnv?: string;
|
|
198
|
+
allowedUserIds?: number[];
|
|
195
199
|
}
|
|
196
200
|
declare class TelegramAdapter implements MessagingAdapter {
|
|
197
201
|
readonly platform: "telegram";
|
|
@@ -203,6 +207,7 @@ declare class TelegramAdapter implements MessagingAdapter {
|
|
|
203
207
|
private botId;
|
|
204
208
|
private readonly botTokenEnv;
|
|
205
209
|
private readonly webhookSecretEnv;
|
|
210
|
+
private readonly allowedUserIds;
|
|
206
211
|
private handler;
|
|
207
212
|
private readonly sessionCounters;
|
|
208
213
|
private lastUpdateId;
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ var AgentBridge = class {
|
|
|
33
33
|
await this.adapter.initialize();
|
|
34
34
|
}
|
|
35
35
|
async handleMessage(message) {
|
|
36
|
+
const MAX_CONTINUATIONS = 10;
|
|
36
37
|
let cleanup;
|
|
37
38
|
this.adapter.resetRequestState?.();
|
|
38
39
|
try {
|
|
@@ -46,33 +47,53 @@ var AgentBridge = class {
|
|
|
46
47
|
const titleParts = [platformTag, senderLabel];
|
|
47
48
|
if (message.subject) titleParts.push(message.subject);
|
|
48
49
|
const title = titleParts.join(" ") || `${message.platform} thread`;
|
|
49
|
-
const conversation = await this.runner.getOrCreateConversation(
|
|
50
|
-
conversationId,
|
|
51
|
-
{
|
|
52
|
-
platform: message.platform,
|
|
53
|
-
ownerId: this.ownerIdOverride ?? message.sender.id,
|
|
54
|
-
title
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
50
|
const senderLine = message.sender.name ? `From: ${message.sender.name} <${message.sender.id}>` : `From: ${message.sender.id}`;
|
|
58
51
|
const subjectLine = message.subject ? `Subject: ${message.subject}` : "";
|
|
59
52
|
const header = [senderLine, subjectLine].filter(Boolean).join("\n");
|
|
60
|
-
|
|
53
|
+
let currentTask = `${header}
|
|
61
54
|
|
|
62
55
|
${message.text}`;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
let currentFiles = message.files;
|
|
57
|
+
let accumulatedResponse = "";
|
|
58
|
+
let accumulatedFiles = [];
|
|
59
|
+
let totalSteps = 0;
|
|
60
|
+
let stepBudget = 0;
|
|
61
|
+
let continuationCount = 0;
|
|
62
|
+
while (true) {
|
|
63
|
+
const conversation = await this.runner.getOrCreateConversation(
|
|
64
|
+
conversationId,
|
|
65
|
+
{
|
|
66
|
+
platform: message.platform,
|
|
67
|
+
ownerId: this.ownerIdOverride ?? message.sender.id,
|
|
68
|
+
title
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
const result = await this.runner.run(conversationId, {
|
|
72
|
+
task: currentTask,
|
|
73
|
+
messages: conversation.messages,
|
|
74
|
+
files: currentFiles,
|
|
75
|
+
metadata: {
|
|
76
|
+
platform: message.platform,
|
|
77
|
+
sender: message.sender,
|
|
78
|
+
threadId: message.threadRef.platformThreadId
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
accumulatedResponse += result.response;
|
|
82
|
+
if (result.files) accumulatedFiles.push(...result.files);
|
|
83
|
+
totalSteps += result.steps ?? 0;
|
|
84
|
+
if (typeof result.maxSteps === "number") stepBudget = result.maxSteps;
|
|
85
|
+
if (result.continuation && continuationCount < MAX_CONTINUATIONS && (stepBudget <= 0 || totalSteps < stepBudget)) {
|
|
86
|
+
continuationCount++;
|
|
87
|
+
console.log(`[agent-bridge] continuation ${continuationCount}/${MAX_CONTINUATIONS} for ${conversationId}`);
|
|
88
|
+
currentTask = "Continue";
|
|
89
|
+
currentFiles = void 0;
|
|
90
|
+
continue;
|
|
71
91
|
}
|
|
72
|
-
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
73
94
|
if (this.adapter.autoReply) {
|
|
74
|
-
await this.adapter.sendReply(message.threadRef,
|
|
75
|
-
files:
|
|
95
|
+
await this.adapter.sendReply(message.threadRef, accumulatedResponse, {
|
|
96
|
+
files: accumulatedFiles.length > 0 ? accumulatedFiles : void 0
|
|
76
97
|
});
|
|
77
98
|
} else if (!this.adapter.hasSentInCurrentRequest) {
|
|
78
99
|
console.warn("[agent-bridge] tool mode completed without send_email being called; no reply sent");
|
|
@@ -1136,12 +1157,14 @@ var TelegramAdapter = class {
|
|
|
1136
1157
|
botId = 0;
|
|
1137
1158
|
botTokenEnv;
|
|
1138
1159
|
webhookSecretEnv;
|
|
1160
|
+
allowedUserIds;
|
|
1139
1161
|
handler;
|
|
1140
1162
|
sessionCounters = /* @__PURE__ */ new Map();
|
|
1141
1163
|
lastUpdateId = 0;
|
|
1142
1164
|
constructor(options = {}) {
|
|
1143
1165
|
this.botTokenEnv = options.botTokenEnv ?? "TELEGRAM_BOT_TOKEN";
|
|
1144
1166
|
this.webhookSecretEnv = options.webhookSecretEnv ?? "TELEGRAM_WEBHOOK_SECRET";
|
|
1167
|
+
this.allowedUserIds = options.allowedUserIds && options.allowedUserIds.length > 0 ? options.allowedUserIds : void 0;
|
|
1145
1168
|
}
|
|
1146
1169
|
// -----------------------------------------------------------------------
|
|
1147
1170
|
// MessagingAdapter implementation
|
|
@@ -1247,6 +1270,13 @@ var TelegramAdapter = class {
|
|
|
1247
1270
|
res.end();
|
|
1248
1271
|
return;
|
|
1249
1272
|
}
|
|
1273
|
+
if (this.allowedUserIds && message.from) {
|
|
1274
|
+
if (!this.allowedUserIds.includes(message.from.id)) {
|
|
1275
|
+
res.writeHead(200);
|
|
1276
|
+
res.end();
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1250
1280
|
const chatId = String(message.chat.id);
|
|
1251
1281
|
const chatType = message.chat.type;
|
|
1252
1282
|
const isGroup = chatType === "group" || chatType === "supergroup";
|
package/package.json
CHANGED
|
@@ -29,6 +29,7 @@ const NEW_COMMAND_RE = /^\/new(?:@(\S+))?$/i;
|
|
|
29
29
|
export interface TelegramAdapterOptions {
|
|
30
30
|
botTokenEnv?: string;
|
|
31
31
|
webhookSecretEnv?: string;
|
|
32
|
+
allowedUserIds?: number[];
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const collectBody = (req: http.IncomingMessage): Promise<string> =>
|
|
@@ -50,6 +51,7 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
50
51
|
private botId = 0;
|
|
51
52
|
private readonly botTokenEnv: string;
|
|
52
53
|
private readonly webhookSecretEnv: string;
|
|
54
|
+
private readonly allowedUserIds: number[] | undefined;
|
|
53
55
|
private handler: IncomingMessageHandler | undefined;
|
|
54
56
|
private readonly sessionCounters = new Map<string, number>();
|
|
55
57
|
private lastUpdateId = 0;
|
|
@@ -58,6 +60,10 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
58
60
|
this.botTokenEnv = options.botTokenEnv ?? "TELEGRAM_BOT_TOKEN";
|
|
59
61
|
this.webhookSecretEnv =
|
|
60
62
|
options.webhookSecretEnv ?? "TELEGRAM_WEBHOOK_SECRET";
|
|
63
|
+
this.allowedUserIds =
|
|
64
|
+
options.allowedUserIds && options.allowedUserIds.length > 0
|
|
65
|
+
? options.allowedUserIds
|
|
66
|
+
: undefined;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
// -----------------------------------------------------------------------
|
|
@@ -197,6 +203,15 @@ export class TelegramAdapter implements MessagingAdapter {
|
|
|
197
203
|
return;
|
|
198
204
|
}
|
|
199
205
|
|
|
206
|
+
// -- User allowlist -----------------------------------------------------
|
|
207
|
+
if (this.allowedUserIds && message.from) {
|
|
208
|
+
if (!this.allowedUserIds.includes(message.from.id)) {
|
|
209
|
+
res.writeHead(200);
|
|
210
|
+
res.end();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
200
215
|
const chatId = String(message.chat.id);
|
|
201
216
|
const chatType = message.chat.type;
|
|
202
217
|
const isGroup = chatType === "group" || chatType === "supergroup";
|
package/src/bridge.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type {
|
|
3
3
|
AgentBridgeOptions,
|
|
4
|
+
FileAttachment,
|
|
4
5
|
IncomingMessage,
|
|
5
6
|
MessagingAdapter,
|
|
6
7
|
AgentRunner,
|
|
@@ -53,6 +54,7 @@ export class AgentBridge {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
private async handleMessage(message: IncomingMessage): Promise<void> {
|
|
57
|
+
const MAX_CONTINUATIONS = 10;
|
|
56
58
|
let cleanup: (() => Promise<void>) | undefined;
|
|
57
59
|
|
|
58
60
|
this.adapter.resetRequestState?.();
|
|
@@ -71,36 +73,64 @@ export class AgentBridge {
|
|
|
71
73
|
if (message.subject) titleParts.push(message.subject);
|
|
72
74
|
const title = titleParts.join(" ") || `${message.platform} thread`;
|
|
73
75
|
|
|
74
|
-
const conversation = await this.runner.getOrCreateConversation(
|
|
75
|
-
conversationId,
|
|
76
|
-
{
|
|
77
|
-
platform: message.platform,
|
|
78
|
-
ownerId: this.ownerIdOverride ?? message.sender.id,
|
|
79
|
-
title,
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
|
|
83
76
|
const senderLine = message.sender.name
|
|
84
77
|
? `From: ${message.sender.name} <${message.sender.id}>`
|
|
85
78
|
: `From: ${message.sender.id}`;
|
|
86
79
|
const subjectLine = message.subject ? `Subject: ${message.subject}` : "";
|
|
87
80
|
const header = [senderLine, subjectLine].filter(Boolean).join("\n");
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
81
|
+
|
|
82
|
+
let currentTask = `${header}\n\n${message.text}`;
|
|
83
|
+
let currentFiles = message.files;
|
|
84
|
+
let accumulatedResponse = "";
|
|
85
|
+
let accumulatedFiles: FileAttachment[] = [];
|
|
86
|
+
let totalSteps = 0;
|
|
87
|
+
let stepBudget = 0;
|
|
88
|
+
let continuationCount = 0;
|
|
89
|
+
|
|
90
|
+
while (true) {
|
|
91
|
+
const conversation = await this.runner.getOrCreateConversation(
|
|
92
|
+
conversationId,
|
|
93
|
+
{
|
|
94
|
+
platform: message.platform,
|
|
95
|
+
ownerId: this.ownerIdOverride ?? message.sender.id,
|
|
96
|
+
title,
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const result = await this.runner.run(conversationId, {
|
|
101
|
+
task: currentTask,
|
|
102
|
+
messages: conversation.messages,
|
|
103
|
+
files: currentFiles,
|
|
104
|
+
metadata: {
|
|
105
|
+
platform: message.platform,
|
|
106
|
+
sender: message.sender,
|
|
107
|
+
threadId: message.threadRef.platformThreadId,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
accumulatedResponse += result.response;
|
|
112
|
+
if (result.files) accumulatedFiles.push(...result.files);
|
|
113
|
+
totalSteps += result.steps ?? 0;
|
|
114
|
+
if (typeof result.maxSteps === "number") stepBudget = result.maxSteps;
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
result.continuation &&
|
|
118
|
+
continuationCount < MAX_CONTINUATIONS &&
|
|
119
|
+
(stepBudget <= 0 || totalSteps < stepBudget)
|
|
120
|
+
) {
|
|
121
|
+
continuationCount++;
|
|
122
|
+
console.log(`[agent-bridge] continuation ${continuationCount}/${MAX_CONTINUATIONS} for ${conversationId}`);
|
|
123
|
+
currentTask = "Continue";
|
|
124
|
+
currentFiles = undefined;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
100
130
|
|
|
101
131
|
if (this.adapter.autoReply) {
|
|
102
|
-
await this.adapter.sendReply(message.threadRef,
|
|
103
|
-
files:
|
|
132
|
+
await this.adapter.sendReply(message.threadRef, accumulatedResponse, {
|
|
133
|
+
files: accumulatedFiles.length > 0 ? accumulatedFiles : undefined,
|
|
104
134
|
});
|
|
105
135
|
} else if (!this.adapter.hasSentInCurrentRequest) {
|
|
106
136
|
console.warn("[agent-bridge] tool mode completed without send_email being called; no reply sent");
|