appback-remoteagent 0.13.7 → 0.13.11

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/dist/bot.js CHANGED
@@ -58,13 +58,11 @@ const REPORT_PROTOCOL_PROMPT = [
58
58
  "Do not say 'I will continue' or 'I can continue after you do X' unless the first line is REPORT:blocked.",
59
59
  "After the first line, write only the user-facing report.",
60
60
  "Do not stop at intent like 'I will' or 'I am going to'. Do the work first, then report progress/result, or report blocked.",
61
- "Do not claim that a Telegram message or file was sent unless RemoteAgent explicitly confirmed that delivery step.",
62
61
  "If REPORT:result claims code, DB, deploy, commit, push, file delivery, or verification work is complete, include concrete evidence such as file paths, commands, logs, commit IDs, digests, or line references.",
63
- "If you want RemoteAgent to send a file, include a separate line exactly like: TELEGRAM_FILE: /absolute/path/to/file",
64
- "Do not use removed local reporting scripts for Telegram delivery.",
65
- "Do not use raw Telegram tokens or TELEGRAM_* environment variables.",
66
- "Telegram sends are allowed only when the user explicitly instructed that flow and the bot token is retrieved through `node \"$REMOTEAGENT_SECRET_BIN\" get <KEY>`. Never print or persist the token.",
67
- "Do not claim Telegram message delivery unless RemoteAgent handled `TELEGRAM_FILE` delivery or you include concrete API response evidence from an explicit secret-managed Telegram send.",
62
+ "RemoteAgent owns conversational delivery: return text normally and RemoteAgent sends it to the current incoming chat.",
63
+ "RemoteAgent owns attachment delivery: include a separate line exactly like `TELEGRAM_FILE: /absolute/path/to/file` and RemoteAgent sends that file to the current incoming chat.",
64
+ "Product/service Telegram notifications are product code behavior; use the project's secret/config path and keep tokens out of output.",
65
+ "Use project secrets or `node \"$REMOTEAGENT_SECRET_BIN\" get <KEY>` for sensitive runtime values, and never print secret values.",
68
66
  "REMOTEAGENT_SESSION_ID and REMOTEAGENT_PUBLIC_SESSION_ID are available during provider execution. For cron, persist the literal public session id in the cron command instead of assuming the env will still exist later.",
69
67
  ].join("\n");
70
68
  const RECOGNIZED_COMMANDS = new Set([
@@ -1092,8 +1090,9 @@ async function runWithPendingAnimation(botToken, chatId, task) {
1092
1090
  if (progressChunks.length === 0 && normalized.documents.length === 0) {
1093
1091
  return;
1094
1092
  }
1095
- const extra = parseMode ? { parse_mode: parseMode } : undefined;
1096
- for (const chunk of progressChunks) {
1093
+ const rendered = formatProviderTelegramChunks(progressChunks, parseMode);
1094
+ const extra = rendered.parseMode ? { parse_mode: rendered.parseMode } : undefined;
1095
+ for (const chunk of rendered.chunks) {
1097
1096
  await sendTelegramMessage(botToken, chatId, chunk, extra);
1098
1097
  }
1099
1098
  if (normalized.documents.length > 0) {
@@ -1104,12 +1103,13 @@ async function runWithPendingAnimation(botToken, chatId, task) {
1104
1103
  const result = await task(helpers);
1105
1104
  const normalized = await normalizeTelegramDelivery(result.chunks);
1106
1105
  const chunks = flattenChunks(normalized.chunks, 3900);
1107
- const extra = result.parseMode ? { parse_mode: result.parseMode } : undefined;
1108
1106
  if (chunks.length === 0 && normalized.documents.length === 0) {
1109
1107
  await sendTelegramMessage(botToken, chatId, "Response was empty.");
1110
1108
  return;
1111
1109
  }
1112
- for (const chunk of chunks) {
1110
+ const rendered = formatProviderTelegramChunks(chunks, result.parseMode);
1111
+ const extra = rendered.parseMode ? { parse_mode: rendered.parseMode } : undefined;
1112
+ for (const chunk of rendered.chunks) {
1113
1113
  await sendTelegramMessage(botToken, chatId, chunk, extra);
1114
1114
  }
1115
1115
  if (normalized.documents.length > 0) {
@@ -1359,6 +1359,56 @@ function parseReportResponses(formattedBlocks, transform) {
1359
1359
  const chunks = transform(parsedBlocks.map((item) => item.text));
1360
1360
  return { kind, chunks };
1361
1361
  }
1362
+ function formatProviderTelegramChunks(chunks, explicitParseMode) {
1363
+ if (explicitParseMode) {
1364
+ return { chunks, parseMode: explicitParseMode };
1365
+ }
1366
+ return {
1367
+ chunks: chunks.map((chunk) => renderTelegramHtml(chunk)),
1368
+ parseMode: "HTML",
1369
+ };
1370
+ }
1371
+ function renderTelegramHtml(text) {
1372
+ const lines = text.split(/\r?\n/);
1373
+ const rendered = [];
1374
+ let codeFence;
1375
+ for (const line of lines) {
1376
+ if (/^```\w*\s*$/.test(line.trim())) {
1377
+ if (codeFence) {
1378
+ rendered.push(`<pre>${escapeTelegramHtml(codeFence.join("\n"))}</pre>`);
1379
+ codeFence = undefined;
1380
+ }
1381
+ else {
1382
+ codeFence = [];
1383
+ }
1384
+ continue;
1385
+ }
1386
+ if (codeFence) {
1387
+ codeFence.push(line);
1388
+ continue;
1389
+ }
1390
+ rendered.push(renderTelegramInlineHtml(line));
1391
+ }
1392
+ if (codeFence) {
1393
+ rendered.push(`<pre>${escapeTelegramHtml(codeFence.join("\n"))}</pre>`);
1394
+ }
1395
+ return rendered.join("\n");
1396
+ }
1397
+ function renderTelegramInlineHtml(line) {
1398
+ const parts = line.split(/(`[^`\n]+`)/g);
1399
+ return parts.map((part) => {
1400
+ if (part.startsWith("`") && part.endsWith("`") && part.length >= 2) {
1401
+ return `<code>${escapeTelegramHtml(part.slice(1, -1))}</code>`;
1402
+ }
1403
+ return escapeTelegramHtml(part);
1404
+ }).join("");
1405
+ }
1406
+ function escapeTelegramHtml(value) {
1407
+ return value
1408
+ .replace(/&/g, "&amp;")
1409
+ .replace(/</g, "&lt;")
1410
+ .replace(/>/g, "&gt;");
1411
+ }
1362
1412
  function looksLikeUntaggedIntentOnlyResponse(text) {
1363
1413
  const normalized = text.trim();
1364
1414
  if (!normalized) {
@@ -2083,12 +2133,8 @@ async function normalizeTelegramDelivery(chunks) {
2083
2133
  }
2084
2134
  return kept.join("\n").trim();
2085
2135
  }));
2086
- const nonEmptyChunks = normalizedChunks.filter(Boolean);
2087
- if (documents.size === 0 && nonEmptyChunks.some((chunk) => mentionsTelegramDeliveryClaim(chunk))) {
2088
- throw new Error("\ubaa8\ub378\uc774 \ud154\ub808\uadf8\ub7a8 \uc804\uc1a1 \uc644\ub8cc\ub97c \uc8fc\uc7a5\ud588\uc9c0\ub9cc, RemoteAgent\uac00 \ud655\uc778\ud55c \ud30c\uc77c \uc804\uc1a1 \uc9c0\uc2dc(`TELEGRAM_FILE: /absolute/path/to/file`)\ub294 \ud3ec\ud568\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ud30c\uc77c\uc744 \ubcf4\ub0b4\ub824\uba74 \ud574\ub2f9 \ud615\uc2dd\uc73c\ub85c \uc808\ub300 \uacbd\ub85c\ub97c \uba85\uc2dc\ud574\uc57c \ud569\ub2c8\ub2e4.");
2089
- }
2090
2136
  return {
2091
- chunks: nonEmptyChunks,
2137
+ chunks: normalizedChunks.filter(Boolean),
2092
2138
  documents: [...documents.values()],
2093
2139
  };
2094
2140
  }
@@ -2104,9 +2150,6 @@ async function isReadableTelegramDocument(filePath) {
2104
2150
  return false;
2105
2151
  }
2106
2152
  }
2107
- function mentionsTelegramDeliveryClaim(text) {
2108
- return /(telegram).*(sent|delivered|delivery)|((sent|delivered|delivery).*(telegram))/i.test(text);
2109
- }
2110
2153
  async function sendTelegramDocuments(botToken, chatId, documents) {
2111
2154
  for (const document of documents) {
2112
2155
  await sendTelegramDocument(botToken, chatId, document);
@@ -325,7 +325,9 @@ export class AgentMemoryService {
325
325
  "- Treat the user message as the active instruction unless the user explicitly says otherwise.",
326
326
  "- RemoteAgent state is guidance only; never refuse work because a TODO is missing.",
327
327
  "- Use the workspace/memory/docs pointers before searching broadly.",
328
- "- Do not claim external delivery, dashboard access, deployment, or file transfer without RemoteAgent-confirmed evidence.",
328
+ "- Do not claim external delivery, dashboard access, deployment, or file transfer without concrete evidence.",
329
+ "- RemoteAgent sends normal provider text and TELEGRAM_FILE attachments to the current incoming chat.",
330
+ "- Product/service Telegram notifications belong to product code and should use the project's secret/config path. Never print tokens.",
329
331
  "- If the same action or progress repeats 3 times, stop, mark the blocker, and report the exact blocker.",
330
332
  "- A final report must include concrete evidence: changed files, relevant line references, git diff/status, command output, or log path.",
331
333
  ].join("\n"),
@@ -56,6 +56,16 @@ Responsibilities:
56
56
  - accept attachments and route them to the active session
57
57
  - gate privileged commands such as remote shell
58
58
 
59
+ ### Telegram delivery ownership
60
+
61
+ RemoteAgent owns delivery for the conversation it is handling.
62
+
63
+ - normal provider text is sent by RemoteAgent to the current incoming bot and chat
64
+ - provider attachments are sent by RemoteAgent when the provider includes `TELEGRAM_FILE: /absolute/path/to/file`
65
+ - the provider does not choose a bot token or chat id for RemoteAgent replies or files
66
+ - product or service code Telegram notifications are separate application behavior and should use that project's own secret/config path
67
+ - Telegram tokens and other credentials must stay in secrets/config and must not be printed in provider output
68
+
59
69
  ### Telegram Mini App layer
60
70
 
61
71
  Responsibilities:
@@ -167,4 +177,4 @@ Still improving:
167
177
  - hosted multi-tenant control plane
168
178
  - account pooling or resale
169
179
  - pretending RemoteAgent is the official Codex or Claude desktop product
170
- - treating Telegram itself as the execution backend
180
+ - treating Telegram itself as the execution backend
@@ -161,8 +161,9 @@ As of the latest stabilization work:
161
161
  - Telegram image download works
162
162
  - files are saved under `/home/au2223/.remoteagent/uploads/telegram/...`
163
163
  - duplicate runtime startup has been blocked
164
- - cross-bot direct Telegram token leakage to provider subprocesses has been blocked
165
- - official runtime-mediated file sending is available only through confirmed RemoteAgent transfer paths
164
+ - RemoteAgent-mediated file sending uses `TELEGRAM_FILE: /absolute/path/to/file`
165
+ - RemoteAgent sends conversation replies and `TELEGRAM_FILE` attachments only to the current incoming bot and chat
166
+ - product/service Telegram notifications are not RemoteAgent conversation delivery; they belong in that project's code and should use that project's secret/config path
166
167
 
167
168
  Remaining work is still needed on attachment response policy and richer file UX.
168
169
  The transport/runtime stability issue and the user-facing attachment response quality issue are separate concerns.
@@ -176,6 +177,6 @@ The deeper issues were runtime ownership and transport discipline:
176
177
  - service-managed and manually-started processes overlapped
177
178
  - PID state and actual live processes diverged
178
179
  - Telegram responses could come from an unexpected process generation
179
- - provider subprocesses had to be prevented from directly using Telegram bot tokens
180
+ - RemoteAgent conversation delivery needed a single current incoming bot/chat owner
180
181
 
181
- The single-instance lock, `systemd`-first lifecycle, and runtime-mediated file transfer rules were added specifically to stop that class of bug from recurring.
182
+ The single-instance lock, `systemd`-first lifecycle, and runtime-owned conversation delivery rules were added specifically to stop that class of bug from recurring.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appback-remoteagent",
3
- "version": "0.13.7",
3
+ "version": "0.13.11",
4
4
  "description": "Personal installable session server for continuing local AI work across PC and Telegram",
5
5
  "license": "MIT",
6
6
  "type": "module",