@dreb/telegram 1.16.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 +91 -0
- package/dist/agent-bridge.d.ts +146 -0
- package/dist/agent-bridge.d.ts.map +1 -0
- package/dist/agent-bridge.js +466 -0
- package/dist/agent-bridge.js.map +1 -0
- package/dist/bot.d.ts +11 -0
- package/dist/bot.d.ts.map +1 -0
- package/dist/bot.js +112 -0
- package/dist/bot.js.map +1 -0
- package/dist/bridge-lifecycle.d.ts +17 -0
- package/dist/bridge-lifecycle.d.ts.map +1 -0
- package/dist/bridge-lifecycle.js +71 -0
- package/dist/bridge-lifecycle.js.map +1 -0
- package/dist/commands/agent.d.ts +11 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +171 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/buddy.d.ts +20 -0
- package/dist/commands/buddy.d.ts.map +1 -0
- package/dist/commands/buddy.js +84 -0
- package/dist/commands/buddy.js.map +1 -0
- package/dist/commands/core.d.ts +13 -0
- package/dist/commands/core.d.ts.map +1 -0
- package/dist/commands/core.js +107 -0
- package/dist/commands/core.js.map +1 -0
- package/dist/commands/index.d.ts +16 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +132 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/refresh.d.ts +18 -0
- package/dist/commands/refresh.d.ts.map +1 -0
- package/dist/commands/refresh.js +55 -0
- package/dist/commands/refresh.js.map +1 -0
- package/dist/commands/sessions.d.ts +10 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +125 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/skills.d.ts +10 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +48 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/handlers/buddy.d.ts +31 -0
- package/dist/handlers/buddy.d.ts.map +1 -0
- package/dist/handlers/buddy.js +126 -0
- package/dist/handlers/buddy.js.map +1 -0
- package/dist/handlers/events.d.ts +65 -0
- package/dist/handlers/events.d.ts.map +1 -0
- package/dist/handlers/events.js +381 -0
- package/dist/handlers/events.js.map +1 -0
- package/dist/handlers/file.d.ts +11 -0
- package/dist/handlers/file.d.ts.map +1 -0
- package/dist/handlers/file.js +138 -0
- package/dist/handlers/file.js.map +1 -0
- package/dist/handlers/message.d.ts +34 -0
- package/dist/handlers/message.d.ts.map +1 -0
- package/dist/handlers/message.js +262 -0
- package/dist/handlers/message.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/state.d.ts +11 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +47 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/util/files.d.ts +27 -0
- package/dist/util/files.d.ts.map +1 -0
- package/dist/util/files.js +75 -0
- package/dist/util/files.js.map +1 -0
- package/dist/util/telegram.d.ts +60 -0
- package/dist/util/telegram.d.ts.map +1 -0
- package/dist/util/telegram.js +192 -0
- package/dist/util/telegram.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent bridge — manages the RPC connection to a dreb agent process.
|
|
3
|
+
* One bridge per user, handles lifecycle, event subscription, and session management.
|
|
4
|
+
*/
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { RpcClient } from "@dreb/coding-agent/rpc";
|
|
8
|
+
import { log } from "./util/telegram.js";
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the absolute path to the dreb CLI entry point.
|
|
11
|
+
* RpcClient defaults to "dist/cli.js" (relative to cwd), but we need
|
|
12
|
+
* the absolute path since the bot's working dir differs from the dreb repo.
|
|
13
|
+
*/
|
|
14
|
+
function resolveDrebCliPath() {
|
|
15
|
+
// import.meta.resolve finds @dreb/coding-agent/dist/index.js
|
|
16
|
+
const resolved = import.meta.resolve("@dreb/coding-agent");
|
|
17
|
+
const distDir = dirname(fileURLToPath(resolved));
|
|
18
|
+
return join(distDir, "cli.js");
|
|
19
|
+
}
|
|
20
|
+
export class AgentBridge {
|
|
21
|
+
config;
|
|
22
|
+
client = null;
|
|
23
|
+
eventListeners = [];
|
|
24
|
+
_isStreaming = false;
|
|
25
|
+
_sessionFile;
|
|
26
|
+
_sessionId;
|
|
27
|
+
exited = false;
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
}
|
|
31
|
+
/** Whether the RPC process is alive */
|
|
32
|
+
get isAlive() {
|
|
33
|
+
return this.client !== null && !this.exited;
|
|
34
|
+
}
|
|
35
|
+
/** Whether the agent is currently streaming a response */
|
|
36
|
+
get isStreaming() {
|
|
37
|
+
return this._isStreaming;
|
|
38
|
+
}
|
|
39
|
+
/** Current session file path */
|
|
40
|
+
get sessionFile() {
|
|
41
|
+
return this._sessionFile;
|
|
42
|
+
}
|
|
43
|
+
/** Current session ID */
|
|
44
|
+
get sessionId() {
|
|
45
|
+
return this._sessionId;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.
|
|
49
|
+
*/
|
|
50
|
+
async start() {
|
|
51
|
+
if (this.client)
|
|
52
|
+
return;
|
|
53
|
+
this.client = new RpcClient({
|
|
54
|
+
cliPath: resolveDrebCliPath(),
|
|
55
|
+
cwd: this.config.workingDir,
|
|
56
|
+
provider: this.config.provider,
|
|
57
|
+
model: this.config.model,
|
|
58
|
+
args: ["--ui", "telegram"],
|
|
59
|
+
});
|
|
60
|
+
this.exited = false;
|
|
61
|
+
await this.client.start();
|
|
62
|
+
// Subscribe to events and forward to listeners
|
|
63
|
+
// Cast: RpcClient types events as AgentEvent but actually forwards all AgentSessionEvent types
|
|
64
|
+
this.client.onEvent((event) => {
|
|
65
|
+
this.handleEvent(event);
|
|
66
|
+
});
|
|
67
|
+
// Detect process exit
|
|
68
|
+
// RpcClient doesn't expose a direct "on exit" — we detect it when send() fails
|
|
69
|
+
log("[BRIDGE] RPC process started");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resume the most recent session, or do nothing if no sessions exist.
|
|
73
|
+
*/
|
|
74
|
+
async resumeLatest() {
|
|
75
|
+
if (!this.client)
|
|
76
|
+
return false;
|
|
77
|
+
try {
|
|
78
|
+
const sessions = await this.client.listSessions();
|
|
79
|
+
if (sessions.length === 0)
|
|
80
|
+
return false;
|
|
81
|
+
const latest = sessions[0]; // Already sorted by modified desc
|
|
82
|
+
const result = await this.client.switchSession(latest.path);
|
|
83
|
+
if (!result.cancelled) {
|
|
84
|
+
this._sessionFile = latest.path;
|
|
85
|
+
this._sessionId = latest.id;
|
|
86
|
+
log(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
log(`[BRIDGE] Failed to resume latest session: ${e}`);
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* List available sessions.
|
|
97
|
+
*/
|
|
98
|
+
async listSessions() {
|
|
99
|
+
if (!this.client)
|
|
100
|
+
return [];
|
|
101
|
+
try {
|
|
102
|
+
return await this.client.listSessions();
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
log(`[BRIDGE] Failed to list sessions: ${e}`);
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Switch to a specific session by path.
|
|
111
|
+
*/
|
|
112
|
+
async switchSession(sessionPath) {
|
|
113
|
+
if (!this.client)
|
|
114
|
+
return false;
|
|
115
|
+
try {
|
|
116
|
+
const result = await this.client.switchSession(sessionPath);
|
|
117
|
+
if (!result.cancelled) {
|
|
118
|
+
this._sessionFile = sessionPath;
|
|
119
|
+
const state = await this.client.getState();
|
|
120
|
+
this._sessionId = state.sessionId;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
log(`[BRIDGE] Failed to switch session: ${e}`);
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a new session.
|
|
131
|
+
*/
|
|
132
|
+
async newSession() {
|
|
133
|
+
if (!this.client)
|
|
134
|
+
return false;
|
|
135
|
+
try {
|
|
136
|
+
const result = await this.client.newSession();
|
|
137
|
+
if (!result.cancelled) {
|
|
138
|
+
const state = await this.client.getState();
|
|
139
|
+
this._sessionFile = state.sessionFile;
|
|
140
|
+
this._sessionId = state.sessionId;
|
|
141
|
+
log(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
log(`[BRIDGE] Failed to create new session: ${e}`);
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Send a prompt to the agent.
|
|
152
|
+
*/
|
|
153
|
+
async prompt(message, images) {
|
|
154
|
+
await this.ensureAlive();
|
|
155
|
+
try {
|
|
156
|
+
await this.client.prompt(message, images);
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
this.handleProcessError(e);
|
|
160
|
+
throw e;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Queue a steering message to interrupt the agent mid-run.
|
|
165
|
+
* The agent injects it after the current tool-call batch finishes.
|
|
166
|
+
*/
|
|
167
|
+
async steer(message, images) {
|
|
168
|
+
await this.ensureAlive();
|
|
169
|
+
try {
|
|
170
|
+
await this.client.steer(message, images);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
this.handleProcessError(e);
|
|
174
|
+
throw e;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Queue a follow-up message for after the agent finishes its current run.
|
|
179
|
+
*/
|
|
180
|
+
async followUp(message, images) {
|
|
181
|
+
await this.ensureAlive();
|
|
182
|
+
try {
|
|
183
|
+
await this.client.followUp(message, images);
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
this.handleProcessError(e);
|
|
187
|
+
throw e;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Abort the current operation.
|
|
192
|
+
*/
|
|
193
|
+
async abort() {
|
|
194
|
+
if (!this.client)
|
|
195
|
+
return;
|
|
196
|
+
try {
|
|
197
|
+
await this.client.abort();
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Process may have already exited
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get the dreb version.
|
|
205
|
+
*/
|
|
206
|
+
async getVersion() {
|
|
207
|
+
await this.ensureAlive();
|
|
208
|
+
try {
|
|
209
|
+
return await this.client.getVersion();
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
this.handleProcessError(e);
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get session statistics.
|
|
218
|
+
*/
|
|
219
|
+
async getSessionStats() {
|
|
220
|
+
if (!this.client)
|
|
221
|
+
return null;
|
|
222
|
+
try {
|
|
223
|
+
return await this.client.getSessionStats();
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
this.handleProcessError(e);
|
|
227
|
+
throw e;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get current state.
|
|
232
|
+
*/
|
|
233
|
+
async getState() {
|
|
234
|
+
if (!this.client)
|
|
235
|
+
return null;
|
|
236
|
+
try {
|
|
237
|
+
return await this.client.getState();
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
this.handleProcessError(e);
|
|
241
|
+
throw e;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get available commands (skills, extensions, prompt templates).
|
|
246
|
+
*/
|
|
247
|
+
async getCommands() {
|
|
248
|
+
if (!this.client)
|
|
249
|
+
return [];
|
|
250
|
+
try {
|
|
251
|
+
return await this.client.getCommands();
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
this.handleProcessError(e);
|
|
255
|
+
throw e;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Compact context.
|
|
260
|
+
*/
|
|
261
|
+
async compact() {
|
|
262
|
+
if (!this.client)
|
|
263
|
+
return null;
|
|
264
|
+
try {
|
|
265
|
+
return await this.client.compact();
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
this.handleProcessError(e);
|
|
269
|
+
throw e;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get available models.
|
|
274
|
+
*/
|
|
275
|
+
async getAvailableModels() {
|
|
276
|
+
if (!this.client)
|
|
277
|
+
return [];
|
|
278
|
+
try {
|
|
279
|
+
return await this.client.getAvailableModels();
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
this.handleProcessError(e);
|
|
283
|
+
throw e;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Set model.
|
|
288
|
+
*/
|
|
289
|
+
async setModel(provider, modelId) {
|
|
290
|
+
if (!this.client)
|
|
291
|
+
return null;
|
|
292
|
+
try {
|
|
293
|
+
return await this.client.setModel(provider, modelId);
|
|
294
|
+
}
|
|
295
|
+
catch (e) {
|
|
296
|
+
this.handleProcessError(e);
|
|
297
|
+
throw e;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Set thinking level.
|
|
302
|
+
*/
|
|
303
|
+
async setThinkingLevel(level) {
|
|
304
|
+
if (!this.client)
|
|
305
|
+
return;
|
|
306
|
+
try {
|
|
307
|
+
await this.client.setThinkingLevel(level);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
this.handleProcessError(e);
|
|
311
|
+
throw e;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Get all messages.
|
|
316
|
+
*/
|
|
317
|
+
async getMessages() {
|
|
318
|
+
if (!this.client)
|
|
319
|
+
return [];
|
|
320
|
+
try {
|
|
321
|
+
return await this.client.getMessages();
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
this.handleProcessError(e);
|
|
325
|
+
throw e;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Hatch a new buddy companion. Runs inside the agent process
|
|
330
|
+
* so API keys never cross the process boundary.
|
|
331
|
+
*/
|
|
332
|
+
async buddyHatch() {
|
|
333
|
+
if (!this.client)
|
|
334
|
+
throw new Error("Agent not connected.");
|
|
335
|
+
try {
|
|
336
|
+
return await this.client.buddyHatch();
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
this.handleProcessError(e);
|
|
340
|
+
throw e;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Reroll the buddy companion. Runs inside the agent process
|
|
345
|
+
* so API keys never cross the process boundary.
|
|
346
|
+
*/
|
|
347
|
+
async buddyReroll() {
|
|
348
|
+
if (!this.client)
|
|
349
|
+
throw new Error("Agent not connected.");
|
|
350
|
+
try {
|
|
351
|
+
return await this.client.buddyReroll();
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
this.handleProcessError(e);
|
|
355
|
+
throw e;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get last assistant text.
|
|
360
|
+
*/
|
|
361
|
+
async getLastAssistantText() {
|
|
362
|
+
if (!this.client)
|
|
363
|
+
return null;
|
|
364
|
+
try {
|
|
365
|
+
return await this.client.getLastAssistantText();
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
this.handleProcessError(e);
|
|
369
|
+
throw e;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Refresh session info from the RPC process state.
|
|
374
|
+
*/
|
|
375
|
+
async refreshSessionInfo() {
|
|
376
|
+
if (!this.client)
|
|
377
|
+
return;
|
|
378
|
+
try {
|
|
379
|
+
const state = await this.client.getState();
|
|
380
|
+
this._sessionFile = state.sessionFile;
|
|
381
|
+
this._sessionId = state.sessionId;
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
this.handleProcessError(e);
|
|
385
|
+
// Non-critical — don't re-throw
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Subscribe to agent events.
|
|
390
|
+
*/
|
|
391
|
+
onEvent(listener) {
|
|
392
|
+
this.eventListeners.push(listener);
|
|
393
|
+
return () => {
|
|
394
|
+
const idx = this.eventListeners.indexOf(listener);
|
|
395
|
+
if (idx !== -1)
|
|
396
|
+
this.eventListeners.splice(idx, 1);
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Stop the RPC process.
|
|
401
|
+
*/
|
|
402
|
+
async stop() {
|
|
403
|
+
if (this.client) {
|
|
404
|
+
try {
|
|
405
|
+
await this.client.stop();
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
// Ignore
|
|
409
|
+
}
|
|
410
|
+
this.client = null;
|
|
411
|
+
this.exited = true;
|
|
412
|
+
this.eventListeners = [];
|
|
413
|
+
log("[BRIDGE] RPC process stopped");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// =========================================================================
|
|
417
|
+
// Internal
|
|
418
|
+
// =========================================================================
|
|
419
|
+
handleEvent(event) {
|
|
420
|
+
// Track streaming state
|
|
421
|
+
if (event.type === "agent_start")
|
|
422
|
+
this._isStreaming = true;
|
|
423
|
+
if (event.type === "agent_end") {
|
|
424
|
+
this._isStreaming = false;
|
|
425
|
+
// Capture session info from agent_end messages
|
|
426
|
+
// Session file/id updates happen via getState after prompt
|
|
427
|
+
}
|
|
428
|
+
for (const listener of this.eventListeners) {
|
|
429
|
+
try {
|
|
430
|
+
listener(event);
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
log(`[BRIDGE] Event listener error: ${e}`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
handleProcessError(e) {
|
|
438
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
439
|
+
if (msg.includes("not started") ||
|
|
440
|
+
msg.includes("not running") ||
|
|
441
|
+
msg.includes("EPIPE") ||
|
|
442
|
+
msg.includes("write after end") ||
|
|
443
|
+
msg.includes("Timeout waiting for response") ||
|
|
444
|
+
msg.includes("RPC process exited")) {
|
|
445
|
+
log(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);
|
|
446
|
+
// Kill the child process before dropping the reference to prevent orphans
|
|
447
|
+
if (this.client) {
|
|
448
|
+
this.client.stop().catch(() => { });
|
|
449
|
+
}
|
|
450
|
+
this.exited = true;
|
|
451
|
+
this.client = null;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async ensureAlive() {
|
|
455
|
+
if (!this.client || this.exited) {
|
|
456
|
+
log("[BRIDGE] Restarting dead RPC process");
|
|
457
|
+
this.client = null;
|
|
458
|
+
this.exited = false;
|
|
459
|
+
await this.start();
|
|
460
|
+
// Session selection is handled by ensureBridgeWithSession — not here.
|
|
461
|
+
// The previous session is still the latest and will be picked up by
|
|
462
|
+
// resumeLatest() on the next message.
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
//# sourceMappingURL=agent-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent bridge — manages the RPC connection to a dreb agent process.\n * One bridge per user, handles lifecycle, event subscription, and session management.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { RpcClient, type RpcSessionInfo } from \"@dreb/coding-agent/rpc\";\nimport type { Config } from \"./config.js\";\nimport { log } from \"./util/telegram.js\";\n\n/**\n * Resolve the absolute path to the dreb CLI entry point.\n * RpcClient defaults to \"dist/cli.js\" (relative to cwd), but we need\n * the absolute path since the bot's working dir differs from the dreb repo.\n */\nfunction resolveDrebCliPath(): string {\n\t// import.meta.resolve finds @dreb/coding-agent/dist/index.js\n\tconst resolved = import.meta.resolve(\"@dreb/coding-agent\");\n\tconst distDir = dirname(fileURLToPath(resolved));\n\treturn join(distDir, \"cli.js\");\n}\n\n/** RPC events include both AgentEvent and session-specific events */\ntype RpcEvent = { type: string; [key: string]: any };\n\nexport type AgentEventListener = (event: RpcEvent) => void;\n\nexport class AgentBridge {\n\tprivate client: RpcClient | null = null;\n\tprivate eventListeners: AgentEventListener[] = [];\n\tprivate _isStreaming = false;\n\tprivate _sessionFile: string | undefined;\n\tprivate _sessionId: string | undefined;\n\tprivate exited = false;\n\n\tconstructor(private config: Config) {}\n\n\t/** Whether the RPC process is alive */\n\tget isAlive(): boolean {\n\t\treturn this.client !== null && !this.exited;\n\t}\n\n\t/** Whether the agent is currently streaming a response */\n\tget isStreaming(): boolean {\n\t\treturn this._isStreaming;\n\t}\n\n\t/** Current session file path */\n\tget sessionFile(): string | undefined {\n\t\treturn this._sessionFile;\n\t}\n\n\t/** Current session ID */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Start the RPC process. Does NOT resume a session — call resumeLatest() or newSession() after.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.client) return;\n\n\t\tthis.client = new RpcClient({\n\t\t\tcliPath: resolveDrebCliPath(),\n\t\t\tcwd: this.config.workingDir,\n\t\t\tprovider: this.config.provider,\n\t\t\tmodel: this.config.model,\n\t\t\targs: [\"--ui\", \"telegram\"],\n\t\t});\n\n\t\tthis.exited = false;\n\t\tawait this.client.start();\n\n\t\t// Subscribe to events and forward to listeners\n\t\t// Cast: RpcClient types events as AgentEvent but actually forwards all AgentSessionEvent types\n\t\tthis.client.onEvent((event) => {\n\t\t\tthis.handleEvent(event as RpcEvent);\n\t\t});\n\n\t\t// Detect process exit\n\t\t// RpcClient doesn't expose a direct \"on exit\" — we detect it when send() fails\n\t\tlog(\"[BRIDGE] RPC process started\");\n\t}\n\n\t/**\n\t * Resume the most recent session, or do nothing if no sessions exist.\n\t */\n\tasync resumeLatest(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst sessions = await this.client.listSessions();\n\t\t\tif (sessions.length === 0) return false;\n\n\t\t\tconst latest = sessions[0]; // Already sorted by modified desc\n\t\t\tconst result = await this.client.switchSession(latest.path);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = latest.path;\n\t\t\t\tthis._sessionId = latest.id;\n\t\t\t\tlog(`[BRIDGE] Resumed session ${latest.id.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to resume latest session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * List available sessions.\n\t */\n\tasync listSessions(): Promise<RpcSessionInfo[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.listSessions();\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to list sessions: ${e}`);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Switch to a specific session by path.\n\t */\n\tasync switchSession(sessionPath: string): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.switchSession(sessionPath);\n\t\t\tif (!result.cancelled) {\n\t\t\t\tthis._sessionFile = sessionPath;\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to switch session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new session.\n\t */\n\tasync newSession(): Promise<boolean> {\n\t\tif (!this.client) return false;\n\t\ttry {\n\t\t\tconst result = await this.client.newSession();\n\t\t\tif (!result.cancelled) {\n\t\t\t\tconst state = await this.client.getState();\n\t\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\t\tthis._sessionId = state.sessionId;\n\t\t\t\tlog(`[BRIDGE] New session ${state.sessionId.slice(0, 8)}`);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tlog(`[BRIDGE] Failed to create new session: ${e}`);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Send a prompt to the agent.\n\t */\n\tasync prompt(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.prompt(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a steering message to interrupt the agent mid-run.\n\t * The agent injects it after the current tool-call batch finishes.\n\t */\n\tasync steer(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.steer(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Queue a follow-up message for after the agent finishes its current run.\n\t */\n\tasync followUp(message: string, images?: Array<{ type: \"image\"; data: string; mimeType: string }>): Promise<void> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\tawait this.client!.followUp(message, images);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Abort the current operation.\n\t */\n\tasync abort(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.abort();\n\t\t} catch {\n\t\t\t// Process may have already exited\n\t\t}\n\t}\n\n\t/**\n\t * Get the dreb version.\n\t */\n\tasync getVersion(): Promise<string> {\n\t\tawait this.ensureAlive();\n\t\ttry {\n\t\t\treturn await this.client!.getVersion();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get session statistics.\n\t */\n\tasync getSessionStats(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getSessionStats();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get current state.\n\t */\n\tasync getState(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getState();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available commands (skills, extensions, prompt templates).\n\t */\n\tasync getCommands(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getCommands();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Compact context.\n\t */\n\tasync compact(): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.compact();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get available models.\n\t */\n\tasync getAvailableModels(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getAvailableModels();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set model.\n\t */\n\tasync setModel(provider: string, modelId: string): Promise<any> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.setModel(provider, modelId);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Set thinking level.\n\t */\n\tasync setThinkingLevel(level: string): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tawait this.client.setThinkingLevel(level as any);\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get all messages.\n\t */\n\tasync getMessages(): Promise<any[]> {\n\t\tif (!this.client) return [];\n\t\ttry {\n\t\t\treturn await this.client.getMessages();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Hatch a new buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyHatch(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyHatch();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Reroll the buddy companion. Runs inside the agent process\n\t * so API keys never cross the process boundary.\n\t */\n\tasync buddyReroll(): Promise<any> {\n\t\tif (!this.client) throw new Error(\"Agent not connected.\");\n\t\ttry {\n\t\t\treturn await this.client.buddyReroll();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Get last assistant text.\n\t */\n\tasync getLastAssistantText(): Promise<string | null> {\n\t\tif (!this.client) return null;\n\t\ttry {\n\t\t\treturn await this.client.getLastAssistantText();\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t/**\n\t * Refresh session info from the RPC process state.\n\t */\n\tasync refreshSessionInfo(): Promise<void> {\n\t\tif (!this.client) return;\n\t\ttry {\n\t\t\tconst state = await this.client.getState();\n\t\t\tthis._sessionFile = state.sessionFile;\n\t\t\tthis._sessionId = state.sessionId;\n\t\t} catch (e) {\n\t\t\tthis.handleProcessError(e);\n\t\t\t// Non-critical — don't re-throw\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to agent events.\n\t */\n\tonEvent(listener: AgentEventListener): () => void {\n\t\tthis.eventListeners.push(listener);\n\t\treturn () => {\n\t\t\tconst idx = this.eventListeners.indexOf(listener);\n\t\t\tif (idx !== -1) this.eventListeners.splice(idx, 1);\n\t\t};\n\t}\n\n\t/**\n\t * Stop the RPC process.\n\t */\n\tasync stop(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.stop();\n\t\t\t} catch {\n\t\t\t\t// Ignore\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t\tthis.exited = true;\n\t\t\tthis.eventListeners = [];\n\t\t\tlog(\"[BRIDGE] RPC process stopped\");\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Internal\n\t// =========================================================================\n\n\tprivate handleEvent(event: RpcEvent): void {\n\t\t// Track streaming state\n\t\tif (event.type === \"agent_start\") this._isStreaming = true;\n\t\tif (event.type === \"agent_end\") {\n\t\t\tthis._isStreaming = false;\n\t\t\t// Capture session info from agent_end messages\n\t\t\t// Session file/id updates happen via getState after prompt\n\t\t}\n\n\t\tfor (const listener of this.eventListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(event);\n\t\t\t} catch (e) {\n\t\t\t\tlog(`[BRIDGE] Event listener error: ${e}`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleProcessError(e: unknown): void {\n\t\tconst msg = e instanceof Error ? e.message : String(e);\n\t\tif (\n\t\t\tmsg.includes(\"not started\") ||\n\t\t\tmsg.includes(\"not running\") ||\n\t\t\tmsg.includes(\"EPIPE\") ||\n\t\t\tmsg.includes(\"write after end\") ||\n\t\t\tmsg.includes(\"Timeout waiting for response\") ||\n\t\t\tmsg.includes(\"RPC process exited\")\n\t\t) {\n\t\t\tlog(`[BRIDGE] RPC process exited or hung: ${msg.slice(0, 100)}`);\n\t\t\t// Kill the child process before dropping the reference to prevent orphans\n\t\t\tif (this.client) {\n\t\t\t\tthis.client.stop().catch(() => {});\n\t\t\t}\n\t\t\tthis.exited = true;\n\t\t\tthis.client = null;\n\t\t}\n\t}\n\n\tprivate async ensureAlive(): Promise<void> {\n\t\tif (!this.client || this.exited) {\n\t\t\tlog(\"[BRIDGE] Restarting dead RPC process\");\n\t\t\tthis.client = null;\n\t\t\tthis.exited = false;\n\t\t\tawait this.start();\n\t\t\t// Session selection is handled by ensureBridgeWithSession — not here.\n\t\t\t// The previous session is still the latest and will be picked up by\n\t\t\t// resumeLatest() on the next message.\n\t\t}\n\t}\n}\n"]}
|
package/dist/bot.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.
|
|
3
|
+
*/
|
|
4
|
+
import { Bot } from "grammy";
|
|
5
|
+
import { setMyCommands } from "./commands/index.js";
|
|
6
|
+
import type { Config } from "./config.js";
|
|
7
|
+
import type { UserState } from "./types.js";
|
|
8
|
+
declare function getUserState(userId: number, config?: Config): UserState;
|
|
9
|
+
export declare function createBot(config: Config): Bot;
|
|
10
|
+
export { getUserState, setMyCommands };
|
|
11
|
+
//# sourceMappingURL=bot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7B,OAAO,EAAoB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqB5C,iBAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAQhE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAiF7C;AAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC","sourcesContent":["/**\n * Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.\n */\n\nimport { Bot } from \"grammy\";\nimport { ensureBridgeWithSession } from \"./bridge-lifecycle.js\";\nimport { registerCommands, setMyCommands } from \"./commands/index.js\";\nimport type { Config } from \"./config.js\";\nimport { handleFile } from \"./handlers/file.js\";\nimport { sendPrompt } from \"./handlers/message.js\";\nimport type { UserState } from \"./types.js\";\nimport { log, safeDelete, safeSend } from \"./util/telegram.js\";\n\n/** Per-user state store */\nconst userStates = new Map<number, UserState>();\n\nfunction createUserState(config: Config): UserState {\n\treturn {\n\t\tbridge: null,\n\t\tconfig,\n\t\tpromptInFlight: false,\n\t\tnewSessionFlag: false,\n\t\tnewSessionCwd: null,\n\t\teffectiveCwd: null,\n\t\tbackgroundAgents: new Map(),\n\t\tstopRequested: false,\n\t\toutbox: [],\n\t\tbuddyController: null,\n\t};\n}\n\nfunction getUserState(userId: number, config?: Config): UserState {\n\tlet state = userStates.get(userId);\n\tif (!state) {\n\t\tif (!config) throw new Error(\"Config required for new user state creation\");\n\t\tstate = createUserState(config);\n\t\tuserStates.set(userId, state);\n\t}\n\treturn state;\n}\n\nexport function createBot(config: Config): Bot {\n\tconst bot = new Bot(config.botToken);\n\n\t// Auth middleware — check allowed user IDs\n\tif (config.allowedUserIds.length > 0) {\n\t\tbot.use(async (ctx, next) => {\n\t\t\tconst userId = ctx.from?.id;\n\t\t\tif (!userId || !config.allowedUserIds.includes(userId)) {\n\t\t\t\tlog(`[AUTH] Rejected user ${userId}`);\n\t\t\t\tawait ctx.reply(\"⛔ Not authorized\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait next();\n\t\t});\n\t}\n\n\t// Register slash commands — bind config so callers see (userId) => UserState\n\tconst boundGetUserState = (userId: number) => getUserState(userId, config);\n\tregisterCommands(bot, config, boundGetUserState);\n\n\t// Text message handler\n\tbot.on(\"message:text\", async (ctx) => {\n\t\t// Skip commands (already handled above)\n\t\tif (ctx.message.text.startsWith(\"/\")) return;\n\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\t\tconst isBusy = userState.bridge?.isStreaming || userState.promptInFlight;\n\n\t\t// Show status immediately\n\t\tconst statusText = isBusy ? \"↩️ _Steering..._\" : \"🧠 _Thinking..._\";\n\t\tlet statusMessageId: number | null = null;\n\t\ttry {\n\t\t\tconst sent = await ctx.reply(statusText, { parse_mode: \"Markdown\" });\n\t\t\tstatusMessageId = sent.message_id;\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Failed to send status: ${e}`);\n\t\t}\n\n\t\t// Ensure bridge is alive and session is set up\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Bridge setup failed: ${e}`);\n\t\t\tif (statusMessageId) void safeDelete(ctx.api, ctx.chat!.id, statusMessageId);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tsendPrompt(ctx.api, userState, {\n\t\t\tchatId: ctx.chat!.id,\n\t\t\treplyToId: ctx.message.message_id,\n\t\t\tuserId,\n\t\t\tprompt: ctx.message.text,\n\t\t\tstatusMessageId,\n\t\t});\n\t});\n\n\t// File handler (documents, photos, voice, audio, video)\n\tbot.on([\"message:document\", \"message:photo\", \"message:voice\", \"message:audio\", \"message:video\"], async (ctx) => {\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\n\t\t// Ensure bridge is alive\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[FILE] Bridge setup failed: ${e}`);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait handleFile(ctx, ctx.api, boundGetUserState);\n\t});\n\n\t// Error handler\n\tbot.catch((err) => {\n\t\tlog(`[ERROR] ${err.error}`);\n\t});\n\n\treturn bot;\n}\n\nexport { getUserState, setMyCommands };\n"]}
|
package/dist/bot.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.
|
|
3
|
+
*/
|
|
4
|
+
import { Bot } from "grammy";
|
|
5
|
+
import { ensureBridgeWithSession } from "./bridge-lifecycle.js";
|
|
6
|
+
import { registerCommands, setMyCommands } from "./commands/index.js";
|
|
7
|
+
import { handleFile } from "./handlers/file.js";
|
|
8
|
+
import { sendPrompt } from "./handlers/message.js";
|
|
9
|
+
import { log, safeDelete, safeSend } from "./util/telegram.js";
|
|
10
|
+
/** Per-user state store */
|
|
11
|
+
const userStates = new Map();
|
|
12
|
+
function createUserState(config) {
|
|
13
|
+
return {
|
|
14
|
+
bridge: null,
|
|
15
|
+
config,
|
|
16
|
+
promptInFlight: false,
|
|
17
|
+
newSessionFlag: false,
|
|
18
|
+
newSessionCwd: null,
|
|
19
|
+
effectiveCwd: null,
|
|
20
|
+
backgroundAgents: new Map(),
|
|
21
|
+
stopRequested: false,
|
|
22
|
+
outbox: [],
|
|
23
|
+
buddyController: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function getUserState(userId, config) {
|
|
27
|
+
let state = userStates.get(userId);
|
|
28
|
+
if (!state) {
|
|
29
|
+
if (!config)
|
|
30
|
+
throw new Error("Config required for new user state creation");
|
|
31
|
+
state = createUserState(config);
|
|
32
|
+
userStates.set(userId, state);
|
|
33
|
+
}
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
export function createBot(config) {
|
|
37
|
+
const bot = new Bot(config.botToken);
|
|
38
|
+
// Auth middleware — check allowed user IDs
|
|
39
|
+
if (config.allowedUserIds.length > 0) {
|
|
40
|
+
bot.use(async (ctx, next) => {
|
|
41
|
+
const userId = ctx.from?.id;
|
|
42
|
+
if (!userId || !config.allowedUserIds.includes(userId)) {
|
|
43
|
+
log(`[AUTH] Rejected user ${userId}`);
|
|
44
|
+
await ctx.reply("⛔ Not authorized");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
await next();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Register slash commands — bind config so callers see (userId) => UserState
|
|
51
|
+
const boundGetUserState = (userId) => getUserState(userId, config);
|
|
52
|
+
registerCommands(bot, config, boundGetUserState);
|
|
53
|
+
// Text message handler
|
|
54
|
+
bot.on("message:text", async (ctx) => {
|
|
55
|
+
// Skip commands (already handled above)
|
|
56
|
+
if (ctx.message.text.startsWith("/"))
|
|
57
|
+
return;
|
|
58
|
+
const userId = ctx.from.id;
|
|
59
|
+
const userState = getUserState(userId, config);
|
|
60
|
+
const isBusy = userState.bridge?.isStreaming || userState.promptInFlight;
|
|
61
|
+
// Show status immediately
|
|
62
|
+
const statusText = isBusy ? "↩️ _Steering..._" : "🧠 _Thinking..._";
|
|
63
|
+
let statusMessageId = null;
|
|
64
|
+
try {
|
|
65
|
+
const sent = await ctx.reply(statusText, { parse_mode: "Markdown" });
|
|
66
|
+
statusMessageId = sent.message_id;
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
log(`[MSG] Failed to send status: ${e}`);
|
|
70
|
+
}
|
|
71
|
+
// Ensure bridge is alive and session is set up
|
|
72
|
+
try {
|
|
73
|
+
await ensureBridgeWithSession(config, userState);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
log(`[MSG] Bridge setup failed: ${e}`);
|
|
77
|
+
if (statusMessageId)
|
|
78
|
+
void safeDelete(ctx.api, ctx.chat.id, statusMessageId);
|
|
79
|
+
await safeSend(ctx.api, ctx.chat.id, `❌ Failed to start agent: ${e}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
sendPrompt(ctx.api, userState, {
|
|
83
|
+
chatId: ctx.chat.id,
|
|
84
|
+
replyToId: ctx.message.message_id,
|
|
85
|
+
userId,
|
|
86
|
+
prompt: ctx.message.text,
|
|
87
|
+
statusMessageId,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
// File handler (documents, photos, voice, audio, video)
|
|
91
|
+
bot.on(["message:document", "message:photo", "message:voice", "message:audio", "message:video"], async (ctx) => {
|
|
92
|
+
const userId = ctx.from.id;
|
|
93
|
+
const userState = getUserState(userId, config);
|
|
94
|
+
// Ensure bridge is alive
|
|
95
|
+
try {
|
|
96
|
+
await ensureBridgeWithSession(config, userState);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
log(`[FILE] Bridge setup failed: ${e}`);
|
|
100
|
+
await safeSend(ctx.api, ctx.chat.id, `❌ Failed to start agent: ${e}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await handleFile(ctx, ctx.api, boundGetUserState);
|
|
104
|
+
});
|
|
105
|
+
// Error handler
|
|
106
|
+
bot.catch((err) => {
|
|
107
|
+
log(`[ERROR] ${err.error}`);
|
|
108
|
+
});
|
|
109
|
+
return bot;
|
|
110
|
+
}
|
|
111
|
+
export { getUserState, setMyCommands };
|
|
112
|
+
//# sourceMappingURL=bot.js.map
|
package/dist/bot.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.js","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bot setup — creates the grammy Bot, wires up auth, commands, and message handlers.\n */\n\nimport { Bot } from \"grammy\";\nimport { ensureBridgeWithSession } from \"./bridge-lifecycle.js\";\nimport { registerCommands, setMyCommands } from \"./commands/index.js\";\nimport type { Config } from \"./config.js\";\nimport { handleFile } from \"./handlers/file.js\";\nimport { sendPrompt } from \"./handlers/message.js\";\nimport type { UserState } from \"./types.js\";\nimport { log, safeDelete, safeSend } from \"./util/telegram.js\";\n\n/** Per-user state store */\nconst userStates = new Map<number, UserState>();\n\nfunction createUserState(config: Config): UserState {\n\treturn {\n\t\tbridge: null,\n\t\tconfig,\n\t\tpromptInFlight: false,\n\t\tnewSessionFlag: false,\n\t\tnewSessionCwd: null,\n\t\teffectiveCwd: null,\n\t\tbackgroundAgents: new Map(),\n\t\tstopRequested: false,\n\t\toutbox: [],\n\t\tbuddyController: null,\n\t};\n}\n\nfunction getUserState(userId: number, config?: Config): UserState {\n\tlet state = userStates.get(userId);\n\tif (!state) {\n\t\tif (!config) throw new Error(\"Config required for new user state creation\");\n\t\tstate = createUserState(config);\n\t\tuserStates.set(userId, state);\n\t}\n\treturn state;\n}\n\nexport function createBot(config: Config): Bot {\n\tconst bot = new Bot(config.botToken);\n\n\t// Auth middleware — check allowed user IDs\n\tif (config.allowedUserIds.length > 0) {\n\t\tbot.use(async (ctx, next) => {\n\t\t\tconst userId = ctx.from?.id;\n\t\t\tif (!userId || !config.allowedUserIds.includes(userId)) {\n\t\t\t\tlog(`[AUTH] Rejected user ${userId}`);\n\t\t\t\tawait ctx.reply(\"⛔ Not authorized\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait next();\n\t\t});\n\t}\n\n\t// Register slash commands — bind config so callers see (userId) => UserState\n\tconst boundGetUserState = (userId: number) => getUserState(userId, config);\n\tregisterCommands(bot, config, boundGetUserState);\n\n\t// Text message handler\n\tbot.on(\"message:text\", async (ctx) => {\n\t\t// Skip commands (already handled above)\n\t\tif (ctx.message.text.startsWith(\"/\")) return;\n\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\t\tconst isBusy = userState.bridge?.isStreaming || userState.promptInFlight;\n\n\t\t// Show status immediately\n\t\tconst statusText = isBusy ? \"↩️ _Steering..._\" : \"🧠 _Thinking..._\";\n\t\tlet statusMessageId: number | null = null;\n\t\ttry {\n\t\t\tconst sent = await ctx.reply(statusText, { parse_mode: \"Markdown\" });\n\t\t\tstatusMessageId = sent.message_id;\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Failed to send status: ${e}`);\n\t\t}\n\n\t\t// Ensure bridge is alive and session is set up\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[MSG] Bridge setup failed: ${e}`);\n\t\t\tif (statusMessageId) void safeDelete(ctx.api, ctx.chat!.id, statusMessageId);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tsendPrompt(ctx.api, userState, {\n\t\t\tchatId: ctx.chat!.id,\n\t\t\treplyToId: ctx.message.message_id,\n\t\t\tuserId,\n\t\t\tprompt: ctx.message.text,\n\t\t\tstatusMessageId,\n\t\t});\n\t});\n\n\t// File handler (documents, photos, voice, audio, video)\n\tbot.on([\"message:document\", \"message:photo\", \"message:voice\", \"message:audio\", \"message:video\"], async (ctx) => {\n\t\tconst userId = ctx.from!.id;\n\t\tconst userState = getUserState(userId, config);\n\n\t\t// Ensure bridge is alive\n\t\ttry {\n\t\t\tawait ensureBridgeWithSession(config, userState);\n\t\t} catch (e) {\n\t\t\tlog(`[FILE] Bridge setup failed: ${e}`);\n\t\t\tawait safeSend(ctx.api, ctx.chat!.id, `❌ Failed to start agent: ${e}`);\n\t\t\treturn;\n\t\t}\n\n\t\tawait handleFile(ctx, ctx.api, boundGetUserState);\n\t});\n\n\t// Error handler\n\tbot.catch((err) => {\n\t\tlog(`[ERROR] ${err.error}`);\n\t});\n\n\treturn bot;\n}\n\nexport { getUserState, setMyCommands };\n"]}
|