appback-remoteagent 0.14.1 → 0.14.3

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/.env.example CHANGED
@@ -7,6 +7,7 @@ TELEGRAM_EMPTY_RESPONSE_RETRIES=1
7
7
  TELEGRAM_RETRYABLE_ERROR_RETRIES=2
8
8
  TELEGRAM_RETRYABLE_ERROR_DELAY_MS=5000
9
9
  TELEGRAM_UNTAGGED_INTENT_RETRIES=2
10
+ TELEGRAM_POLLING_MAX_CONCURRENCY=8
10
11
  TELEGRAM_SCHEDULER_TICK_MS=1000
11
12
  TELEGRAM_TIERED_POLLING_MIN_BOTS=5
12
13
  TELEGRAM_ACTIVE_POLL_INTERVAL_MS=3000
package/dist/bot.js CHANGED
@@ -1146,6 +1146,7 @@ async function routeTelegramWorkLoop(bridge, botId, chatId, message, label, botM
1146
1146
  let untaggedIntentRetryCount = 0;
1147
1147
  let missingEvidenceRetryCount = 0;
1148
1148
  let deliveredProgressCount = 0;
1149
+ let providerCompleted = false;
1149
1150
  const ensureStillBound = async (phase) => {
1150
1151
  if (!sessionId) {
1151
1152
  return;
@@ -1238,6 +1239,7 @@ async function routeTelegramWorkLoop(bridge, botId, chatId, message, label, botM
1238
1239
  if (currentSession) {
1239
1240
  await memoryService.completeTask(currentSession.session, parsed.chunks.join("\n"));
1240
1241
  }
1242
+ providerCompleted = true;
1241
1243
  autoContinue.clear(botId, chatId, sessionId);
1242
1244
  return parsed.chunks;
1243
1245
  }
@@ -1314,7 +1316,12 @@ async function routeTelegramWorkLoop(bridge, botId, chatId, message, label, botM
1314
1316
  }
1315
1317
  finally {
1316
1318
  if (currentSession) {
1317
- await botManagement.markProviderIdle(botId, sessionId);
1319
+ if (providerCompleted) {
1320
+ await botManagement.markProviderCompleted(botId, sessionId);
1321
+ }
1322
+ else {
1323
+ await botManagement.markProviderIdle(botId, sessionId);
1324
+ }
1318
1325
  }
1319
1326
  }
1320
1327
  }
package/dist/config.js CHANGED
@@ -133,7 +133,7 @@ export const config = {
133
133
  telegramCommandMenuEnabled: readBoolean("TELEGRAM_COMMAND_MENU_ENABLED", true),
134
134
  telegramPollingBackoffMinMs: readTimeout("TELEGRAM_POLLING_BACKOFF_MIN_MS", 60_000),
135
135
  telegramPollingBackoffMaxMs: readTimeout("TELEGRAM_POLLING_BACKOFF_MAX_MS", 900_000),
136
- telegramPollingMaxConcurrency: readTimeout("TELEGRAM_POLLING_MAX_CONCURRENCY", 3),
136
+ telegramPollingMaxConcurrency: readTimeout("TELEGRAM_POLLING_MAX_CONCURRENCY", 8),
137
137
  telegramSchedulerTickMs: readTimeout("TELEGRAM_SCHEDULER_TICK_MS", 1000),
138
138
  telegramTieredPollingMinBots: readTimeout("TELEGRAM_TIERED_POLLING_MIN_BOTS", 5),
139
139
  telegramActivePollIntervalMs: readTimeout("TELEGRAM_ACTIVE_POLL_INTERVAL_MS", 3000),
package/dist/index.js CHANGED
@@ -236,11 +236,13 @@ async function pollTelegramBot(pollingBot, runtime, options) {
236
236
  runtime.offset = orderedUpdates[orderedUpdates.length - 1].update_id + 1;
237
237
  }
238
238
  const receivedMessage = orderedUpdates.some(hasMessagePayload);
239
- const lastMessageAt = receivedMessage ? new Date(now).toISOString() : options.state?.lastMessageAt;
239
+ const lastUserMessageAt = receivedMessage ? new Date(now).toISOString() : options.state?.lastUserMessageAt;
240
+ const lastMessageAt = receivedMessage ? lastUserMessageAt : options.state?.lastMessageAt;
240
241
  const nextPollAt = now + computePolicyPollIntervalMs(options.totalBots, receivedMessage ? 1 : options.botRank, {
241
242
  ...options.state,
242
243
  botId,
243
244
  consecutiveFailures: options.state?.consecutiveFailures ?? runtime.consecutiveFailures,
245
+ lastUserMessageAt,
244
246
  lastMessageAt,
245
247
  }, {
246
248
  tieredPollingMinBots: config.telegramTieredPollingMinBots,
@@ -253,6 +255,7 @@ async function pollTelegramBot(pollingBot, runtime, options) {
253
255
  username: pollingBot.botInfo.username,
254
256
  lastPollAt: new Date(now).toISOString(),
255
257
  lastUpdateAt: orderedUpdates.length > 0 ? new Date(now).toISOString() : undefined,
258
+ lastUserMessageAt,
256
259
  lastMessageAt,
257
260
  nextPollAt: new Date(nextPollAt).toISOString(),
258
261
  consecutiveFailures: 0,
@@ -54,6 +54,11 @@ export class BotManagementService {
54
54
  const pollingBotId = bot ? String(bot.id) : botId;
55
55
  await this.pollingState.markIdle(pollingBotId, sessionId, bot?.username);
56
56
  }
57
+ async markProviderCompleted(botId, sessionId) {
58
+ const bot = await this.findConfiguredBot(botId);
59
+ const pollingBotId = bot ? String(bot.id) : botId;
60
+ await this.pollingState.markCompleted(pollingBotId, sessionId, bot?.username);
61
+ }
57
62
  async getPendingOperationNotice() {
58
63
  const pending = await this.readPendingOperation();
59
64
  if (!pending) {
@@ -70,6 +70,21 @@ export class BotPollingStateService {
70
70
  state.bots[botId] = current;
71
71
  await this.writeNow();
72
72
  }
73
+ async markCompleted(botId, sessionId, username) {
74
+ await this.markIdle(botId, sessionId, username);
75
+ const state = await this.read();
76
+ const current = state.bots[botId] ?? {
77
+ botId,
78
+ username,
79
+ consecutiveFailures: 0,
80
+ };
81
+ if (username) {
82
+ current.username = username;
83
+ }
84
+ current.lastProviderCompletedAt = new Date().toISOString();
85
+ state.bots[botId] = current;
86
+ await this.writeNow();
87
+ }
73
88
  async recordPoll(botId, patch) {
74
89
  const state = await this.read();
75
90
  const current = state.bots[botId] ?? {
@@ -2,7 +2,7 @@ export function computeRecentMessageRanks(botIds, states) {
2
2
  const ranked = botIds
3
3
  .map((botId) => ({
4
4
  botId,
5
- timestamp: parseStateTime(states[botId]?.lastMessageAt) ?? 0,
5
+ timestamp: latestTimestamp(states[botId]?.lastUserMessageAt ?? states[botId]?.lastMessageAt, states[botId]?.lastProviderCompletedAt),
6
6
  }))
7
7
  .sort((left, right) => right.timestamp - left.timestamp);
8
8
  return new Map(ranked.map((entry, index) => [entry.botId, index + 1]));
@@ -29,3 +29,6 @@ function parseStateTime(value) {
29
29
  const timestamp = Date.parse(value);
30
30
  return Number.isFinite(timestamp) ? timestamp : undefined;
31
31
  }
32
+ function latestTimestamp(...values) {
33
+ return Math.max(0, ...values.map((value) => parseStateTime(value) ?? 0));
34
+ }
@@ -14,6 +14,13 @@ When 5 or more bots are configured:
14
14
  - the next 4 idle bots poll every 60 seconds
15
15
  - all remaining idle bots poll every 180 seconds
16
16
 
17
+ Recent activity is updated only when:
18
+
19
+ - the operator sends a message to that bot
20
+ - the provider returns a completed result to that bot
21
+
22
+ Polling itself must not update recent activity. A bot should not become "recent" merely because RemoteAgent checked Telegram for updates.
23
+
17
24
  When a bot has active provider work:
18
25
 
19
26
  - that bot polls every 60 seconds
@@ -30,3 +37,4 @@ RemoteAgent no longer uses deep sleep, wake, or a special main bot for polling c
30
37
  - `/bot doctor` removes bots that Telegram reports as permanently dead.
31
38
  - `/bot remove <username|id>` removes a configured bot from the runtime.
32
39
  - Polling state is stored by Telegram bot id, not by display order.
40
+ - The default polling concurrency is 8 because Telegram long polling can hold each request for up to 30 seconds. Lower values can make later bots look unresponsive when many bots are configured.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appback-remoteagent",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "Personal installable session server for continuing local AI work across PC and Telegram",
5
5
  "license": "MIT",
6
6
  "type": "module",