@ducci/jarvis 1.0.42 → 1.0.43
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/docs/system-prompt.md +1 -1
- package/package.json +1 -1
- package/src/channels/telegram/index.js +36 -18
- package/src/server/config.js +3 -1
- package/src/server/tools.js +15 -4
package/docs/system-prompt.md
CHANGED
|
@@ -52,7 +52,7 @@ There are two types of responses depending on whether you need to use tools:
|
|
|
52
52
|
"logSummary": "A concise explanation of what you did and why, written for a human reading the logs."
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
The `response` value must be a
|
|
55
|
+
The `response` value must be a string — never an array or object. Use HTML formatting tags for readability: <b>bold</b>, <i>italic</i>, <code>inline code</code>, <pre>code blocks</pre>, <blockquote>quotes</blockquote>. Never use Markdown formatting (no **, __, `, or ```). If you need to present structured data (e.g. a list of items), format it as text within the string value.
|
|
56
56
|
|
|
57
57
|
Never include markdown code fences, preamble, or any text outside this JSON object. If you cannot complete a task, explain why in the `response` field — still as valid JSON.
|
|
58
58
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,41 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
1
3
|
import { Bot } from 'grammy';
|
|
2
4
|
import { run } from '@grammyjs/runner';
|
|
3
5
|
import { handleChat } from '../../server/agent.js';
|
|
4
6
|
import { loadSession } from '../../server/sessions.js';
|
|
7
|
+
import { PATHS } from '../../server/config.js';
|
|
5
8
|
import { load, save } from './sessions.js';
|
|
6
9
|
|
|
10
|
+
async function sendMessage(api, chatId, text, sessionId) {
|
|
11
|
+
const MAX_TG = 4096;
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for (let i = 0; i < text.length; i += MAX_TG) {
|
|
14
|
+
chunks.push(text.slice(i, i + MAX_TG));
|
|
15
|
+
}
|
|
16
|
+
for (const chunk of chunks) {
|
|
17
|
+
try {
|
|
18
|
+
await api.sendMessage(chatId, chunk, { parse_mode: 'HTML' });
|
|
19
|
+
} catch (e) {
|
|
20
|
+
if (e.error_code === 400) {
|
|
21
|
+
console.error(`[telegram] HTML parse error chat_id=${chatId}, falling back to plaintext: ${e.description}`);
|
|
22
|
+
if (sessionId) {
|
|
23
|
+
const logFile = path.join(PATHS.logsDir, `session-${sessionId}.jsonl`);
|
|
24
|
+
fs.promises.appendFile(logFile, JSON.stringify({
|
|
25
|
+
ts: new Date().toISOString(),
|
|
26
|
+
sessionId,
|
|
27
|
+
type: 'telegram_parse_error',
|
|
28
|
+
description: e.description || e.message,
|
|
29
|
+
}) + '\n', 'utf8').catch(() => {});
|
|
30
|
+
}
|
|
31
|
+
await api.sendMessage(chatId, chunk);
|
|
32
|
+
} else {
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
7
39
|
export async function startTelegramChannel(config) {
|
|
8
40
|
const { token, allowedUserIds } = config.telegram;
|
|
9
41
|
|
|
@@ -103,23 +135,16 @@ export async function startTelegramChannel(config) {
|
|
|
103
135
|
}
|
|
104
136
|
|
|
105
137
|
try {
|
|
106
|
-
const MAX_TG = 4096;
|
|
107
138
|
const rawResponse = typeof result.response === 'string'
|
|
108
139
|
? result.response
|
|
109
140
|
: result.response != null ? JSON.stringify(result.response, null, 2) : '';
|
|
110
141
|
const text = rawResponse.trim()
|
|
111
142
|
|| 'The agent encountered an error and could not produce a response. Please try again.';
|
|
112
|
-
|
|
113
|
-
await ctx.reply(text);
|
|
114
|
-
} else {
|
|
115
|
-
for (let i = 0; i < text.length; i += MAX_TG) {
|
|
116
|
-
await ctx.reply(text.slice(i, i + MAX_TG));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
143
|
+
await sendMessage(ctx.api, chatId, text, result.sessionId);
|
|
119
144
|
console.log(`[telegram] response sent chat_id=${chatId} length=${text.length}`);
|
|
120
145
|
} catch (e) {
|
|
121
146
|
console.error(`[telegram] delivery error chat_id=${chatId}: ${e.message}`);
|
|
122
|
-
await ctx.
|
|
147
|
+
await ctx.api.sendMessage(chatId, 'Sorry, something went wrong sending the response. Please try again.').catch(() => {});
|
|
123
148
|
} finally {
|
|
124
149
|
clearInterval(typingInterval);
|
|
125
150
|
}
|
|
@@ -162,24 +187,17 @@ export async function startTelegramChannel(config) {
|
|
|
162
187
|
}
|
|
163
188
|
|
|
164
189
|
try {
|
|
165
|
-
const MAX_TG = 4096;
|
|
166
190
|
// Guard against empty or non-string response (e.g. model returns array instead of string)
|
|
167
191
|
const rawResponse = typeof result.response === 'string'
|
|
168
192
|
? result.response
|
|
169
193
|
: result.response != null ? JSON.stringify(result.response, null, 2) : '';
|
|
170
194
|
const text = rawResponse.trim()
|
|
171
195
|
|| 'The agent encountered an error and could not produce a response. Please try again.';
|
|
172
|
-
|
|
173
|
-
await ctx.reply(text);
|
|
174
|
-
} else {
|
|
175
|
-
for (let i = 0; i < text.length; i += MAX_TG) {
|
|
176
|
-
await ctx.reply(text.slice(i, i + MAX_TG));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
196
|
+
await sendMessage(ctx.api, chatId, text, result.sessionId);
|
|
179
197
|
console.log(`[telegram] response sent chat_id=${chatId} length=${text.length}`);
|
|
180
198
|
} catch (e) {
|
|
181
199
|
console.error(`[telegram] delivery error chat_id=${chatId}: ${e.message}`);
|
|
182
|
-
await ctx.
|
|
200
|
+
await ctx.api.sendMessage(chatId, 'Sorry, something went wrong sending the response. Please try again.').catch(() => {});
|
|
183
201
|
} finally {
|
|
184
202
|
clearInterval(typingInterval);
|
|
185
203
|
}
|
package/src/server/config.js
CHANGED
|
@@ -98,7 +98,9 @@ export function resolveSystemPrompt(promptTemplate, sessionId) {
|
|
|
98
98
|
const skills = [];
|
|
99
99
|
for (const entry of entries) {
|
|
100
100
|
if (!entry.isDirectory()) continue;
|
|
101
|
-
const
|
|
101
|
+
const skillFileName = fs.readdirSync(path.join(PATHS.skillsDir, entry.name))
|
|
102
|
+
.find(f => f.toLowerCase() === 'skill.md') || 'skill.md';
|
|
103
|
+
const skillFile = path.join(PATHS.skillsDir, entry.name, skillFileName);
|
|
102
104
|
try {
|
|
103
105
|
const content = fs.readFileSync(skillFile, 'utf8');
|
|
104
106
|
const meta = parseSkillFrontmatter(content);
|
package/src/server/tools.js
CHANGED
|
@@ -472,8 +472,7 @@ const SEED_TOOLS = {
|
|
|
472
472
|
const chatId = settings.channels?.telegram?.allowedUserIds?.[0];
|
|
473
473
|
if (!chatId) return { status: 'error', error: 'No Telegram chat_id configured.' };
|
|
474
474
|
if (!token) return { status: 'error', error: 'No TELEGRAM_BOT_TOKEN configured.' };
|
|
475
|
-
const
|
|
476
|
-
await new Promise((resolve, reject) => {
|
|
475
|
+
const sendRequest = (body) => new Promise((resolve, reject) => {
|
|
477
476
|
const req = https.request({
|
|
478
477
|
hostname: 'api.telegram.org',
|
|
479
478
|
path: '/bot' + token + '/sendMessage',
|
|
@@ -484,7 +483,7 @@ const SEED_TOOLS = {
|
|
|
484
483
|
res.on('data', chunk => data += chunk);
|
|
485
484
|
res.on('end', () => {
|
|
486
485
|
const parsed = JSON.parse(data);
|
|
487
|
-
if (!parsed.ok) reject(new Error(parsed.description));
|
|
486
|
+
if (!parsed.ok) reject(Object.assign(new Error(parsed.description), { description: parsed.description, error_code: parsed.error_code }));
|
|
488
487
|
else resolve(parsed);
|
|
489
488
|
});
|
|
490
489
|
});
|
|
@@ -492,6 +491,15 @@ const SEED_TOOLS = {
|
|
|
492
491
|
req.write(body);
|
|
493
492
|
req.end();
|
|
494
493
|
});
|
|
494
|
+
try {
|
|
495
|
+
await sendRequest(JSON.stringify({ chat_id: chatId, text: args.message, parse_mode: 'HTML' }));
|
|
496
|
+
} catch (e) {
|
|
497
|
+
if (e.error_code === 400) {
|
|
498
|
+
await sendRequest(JSON.stringify({ chat_id: chatId, text: args.message }));
|
|
499
|
+
} else {
|
|
500
|
+
throw e;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
495
503
|
return { status: 'ok', chatId };
|
|
496
504
|
`,
|
|
497
505
|
},
|
|
@@ -540,7 +548,10 @@ const SEED_TOOLS = {
|
|
|
540
548
|
},
|
|
541
549
|
},
|
|
542
550
|
code: `
|
|
543
|
-
const
|
|
551
|
+
const skillDir = path.join(process.env.HOME, '.jarvis/data/skills', args.name);
|
|
552
|
+
const dirFiles = await fs.promises.readdir(skillDir).catch(() => []);
|
|
553
|
+
const skillFileName = dirFiles.find(f => f.toLowerCase() === 'skill.md') || 'skill.md';
|
|
554
|
+
const skillFile = path.join(skillDir, skillFileName);
|
|
544
555
|
const content = await fs.promises.readFile(skillFile, 'utf8').catch(() => null);
|
|
545
556
|
if (!content) return { status: 'not_found', name: args.name };
|
|
546
557
|
return { status: 'ok', name: args.name, content };
|