iobroker.pi-agent 0.0.1 → 0.0.2
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 +6 -0
- package/io-package.json +14 -1
- package/lib/telegram.js +51 -0
- package/main.js +10 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,12 @@ Select **DeepSeek** as provider and enter a key from the DeepSeek platform. Avai
|
|
|
101
101
|
|
|
102
102
|
## Changelog
|
|
103
103
|
|
|
104
|
+
### 0.0.2 (2026-10-25)
|
|
105
|
+
|
|
106
|
+
- Telegram answers keep their formatting: Markdown of the model is sent as Telegram HTML (bold, italic, code, links;
|
|
107
|
+
tables as a monospace block), and Telegram chats are asked for short answers without tables
|
|
108
|
+
- Released through the CI workflow with npm provenance (trusted publishing)
|
|
109
|
+
|
|
104
110
|
### 0.0.1 (2026-10-25)
|
|
105
111
|
|
|
106
112
|
- Initial version: durable agent with the tools `search_objects`, `get_state`, `list_instances` and `set_state`
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "pi-agent",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.0.2": {
|
|
7
|
+
"en": "Telegram answers keep their formatting (bold, italic, code, links); released with npm provenance",
|
|
8
|
+
"de": "Telegram-Antworten behalten ihre Formatierung (fett, kursiv, Code, Links); mit npm-Provenance veröffentlicht",
|
|
9
|
+
"ru": "Ответы в Telegram сохраняют форматирование (жирный, курсив, код, ссылки); опубликовано с npm provenance",
|
|
10
|
+
"pt": "As respostas no Telegram mantêm a formatação (negrito, itálico, código, links); publicado com proveniência npm",
|
|
11
|
+
"nl": "Telegram-antwoorden behouden hun opmaak (vet, cursief, code, links); gepubliceerd met npm-herkomst",
|
|
12
|
+
"fr": "Les réponses Telegram gardent leur mise en forme (gras, italique, code, liens) ; publié avec la provenance npm",
|
|
13
|
+
"it": "Le risposte su Telegram mantengono la formattazione (grassetto, corsivo, codice, link); pubblicato con provenienza npm",
|
|
14
|
+
"es": "Las respuestas en Telegram conservan el formato (negrita, cursiva, código, enlaces); publicado con procedencia npm",
|
|
15
|
+
"pl": "Odpowiedzi w Telegramie zachowują formatowanie (pogrubienie, kursywa, kod, linki); opublikowano z pochodzeniem npm",
|
|
16
|
+
"uk": "Відповіді в Telegram зберігають форматування (жирний, курсив, код, посилання); опубліковано з npm provenance",
|
|
17
|
+
"zh-cn": "Telegram 回答保留格式(粗体、斜体、代码、链接);带 npm 来源证明发布"
|
|
18
|
+
},
|
|
6
19
|
"0.0.1": {
|
|
7
20
|
"en": "Initial version: durable agent with search, read and allowlisted write tools; chat via Telegram",
|
|
8
21
|
"de": "Erste Version: dauerhafter Agent mit Such-, Lese- und Schreibwerkzeugen mit Freigabeliste; Chat über Telegram",
|
package/lib/telegram.js
CHANGED
|
@@ -120,7 +120,58 @@ function splitMessage(text, max = MAX_MESSAGE) {
|
|
|
120
120
|
return parts;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/** Instructions for conversations that are Telegram chats. */
|
|
124
|
+
const TELEGRAM_INSTRUCTIONS =
|
|
125
|
+
'This conversation is a Telegram chat. Keep answers short. Use only simple formatting (bold, italic, inline code, short lists); avoid tables and headings.';
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Escape text for Telegram's HTML parse mode.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} text plain text
|
|
131
|
+
* @returns {string} text with &, < and > escaped
|
|
132
|
+
*/
|
|
133
|
+
function escapeHtml(text) {
|
|
134
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Convert the Markdown a model writes into the small HTML subset Telegram understands (parse_mode "HTML").
|
|
139
|
+
* Everything else is escaped, so a stray "<" in an answer can never break the message.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} markdown answer of the model
|
|
142
|
+
* @returns {string} Telegram HTML
|
|
143
|
+
*/
|
|
144
|
+
function markdownToTelegramHtml(markdown) {
|
|
145
|
+
const kept = [];
|
|
146
|
+
// placeholders use a private-use character that does not occur in an answer
|
|
147
|
+
const keep = html => `${kept.push(html) - 1}`;
|
|
148
|
+
let text = String(markdown || '');
|
|
149
|
+
// fenced code blocks and tables keep their layout in a monospace block
|
|
150
|
+
text = text.replace(/```[^\n]*\n([\s\S]*?)```/g, (_, code) =>
|
|
151
|
+
keep(`<pre>${escapeHtml(code.replace(/\n$/, ''))}</pre>`),
|
|
152
|
+
);
|
|
153
|
+
text = text.replace(/(?:^[ \t]*\|.*\|[ \t]*(?:\n|$))+/gm, table => {
|
|
154
|
+
const rows = table
|
|
155
|
+
.trimEnd()
|
|
156
|
+
.split('\n')
|
|
157
|
+
.map(row => row.trim())
|
|
158
|
+
.filter(row => !/^\|[\s:|-]+\|$/.test(row));
|
|
159
|
+
return `${keep(`<pre>${escapeHtml(rows.join('\n'))}</pre>`)}\n`;
|
|
160
|
+
});
|
|
161
|
+
text = escapeHtml(text);
|
|
162
|
+
text = text.replace(/`([^`\n]+)`/g, (_, code) => keep(`<code>${code}</code>`));
|
|
163
|
+
text = text.replace(/^#{1,6}[ \t]+(.+)$/gm, '<b>$1</b>');
|
|
164
|
+
text = text.replace(/^([ \t]*)[-*+][ \t]+/gm, '$1• ');
|
|
165
|
+
text = text.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
|
|
166
|
+
text = text.replace(/(^|[^*\w])\*(?!\s)([^*\n]+?)\*(?![*\w])/g, '$1<i>$2</i>');
|
|
167
|
+
text = text.replace(/~~(.+?)~~/g, '<s>$1</s>');
|
|
168
|
+
text = text.replace(/\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2">$1</a>');
|
|
169
|
+
return text.replace(/(\d+)/g, (_, i) => kept[Number(i)]);
|
|
170
|
+
}
|
|
171
|
+
|
|
123
172
|
module.exports = {
|
|
173
|
+
TELEGRAM_INSTRUCTIONS,
|
|
174
|
+
markdownToTelegramHtml,
|
|
124
175
|
parseAllowedUsers,
|
|
125
176
|
isAllowedUser,
|
|
126
177
|
parseRequest,
|
package/main.js
CHANGED
|
@@ -13,6 +13,8 @@ const {
|
|
|
13
13
|
stripPrefix,
|
|
14
14
|
telegramRequestId,
|
|
15
15
|
splitMessage,
|
|
16
|
+
markdownToTelegramHtml,
|
|
17
|
+
TELEGRAM_INSTRUCTIONS,
|
|
16
18
|
} = require('./lib/telegram');
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -164,7 +166,7 @@ class PiAgent extends utils.Adapter {
|
|
|
164
166
|
// Chats keep their own conversation; a model change in the settings reaches them too.
|
|
165
167
|
for (const id of Object.values(this.chats)) {
|
|
166
168
|
const conversation = await this.harness.conversation(id, this.context);
|
|
167
|
-
await conversation?.configure({ model: this.model }, this.context);
|
|
169
|
+
await conversation?.configure({ model: this.model, instructions: TELEGRAM_INSTRUCTIONS }, this.context);
|
|
168
170
|
}
|
|
169
171
|
await this.subscribeForeignStatesAsync(`${this.telegram}.communicate.request`);
|
|
170
172
|
const prefix = String(this.config.telegramPrefix || '').trim();
|
|
@@ -191,7 +193,7 @@ class PiAgent extends utils.Adapter {
|
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
const created = await this.harness.createConversation(
|
|
194
|
-
{ ownership: { kind: 'ownerless' }, agent: { model: this.model } },
|
|
196
|
+
{ ownership: { kind: 'ownerless' }, agent: { model: this.model, instructions: TELEGRAM_INSTRUCTIONS } },
|
|
195
197
|
this.context,
|
|
196
198
|
);
|
|
197
199
|
this.chats[target.chatId] = created.id;
|
|
@@ -314,7 +316,12 @@ class PiAgent extends utils.Adapter {
|
|
|
314
316
|
async deliver(target, text) {
|
|
315
317
|
if (target.kind === 'telegram') {
|
|
316
318
|
for (const part of splitMessage(text)) {
|
|
317
|
-
|
|
319
|
+
// Telegram shows Markdown literally; its HTML mode renders bold, italic, code and links
|
|
320
|
+
this.sendTo(this.telegram, 'send', {
|
|
321
|
+
text: markdownToTelegramHtml(part),
|
|
322
|
+
chatId: target.chatId,
|
|
323
|
+
parse_mode: 'HTML',
|
|
324
|
+
});
|
|
318
325
|
}
|
|
319
326
|
return;
|
|
320
327
|
}
|