@ducci/jarvis 1.0.69 → 1.0.71
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
CHANGED
|
@@ -64,6 +64,7 @@ Never include markdown code fences, preamble, or any text outside this JSON obje
|
|
|
64
64
|
You have access to a set of tools. Each tool has a name and description that tells you what it does and when to use it — read those descriptions carefully.
|
|
65
65
|
|
|
66
66
|
- Always use a tool to perform an action. Never claim to have done something without actually calling the relevant tool.
|
|
67
|
+
- If answering a question requires checking a file, reading data, or calling any tool, do it first — never send a response that announces what you are about to do. Perform the action, then report the result.
|
|
67
68
|
- Call tools one at a time. You will receive the result before deciding on the next step. Exception: when using `spawn_subagent` for bulk tasks (e.g. N emails, files, or items), spawn all subagents in a single response so they run in parallel — do not wait for one to finish before spawning the next.
|
|
68
69
|
- After a tool call, verify the result before proceeding. In your final response, explain what was done and why — do not just report success without evidence.
|
|
69
70
|
- Stop as soon as the task is complete and verified. Do not do extra work that was not asked for.
|
package/package.json
CHANGED
|
@@ -20,10 +20,28 @@ async function appendTelegramChatLog(chatId, sessionId, direction, text, ts = nu
|
|
|
20
20
|
await fs.promises.appendFile(logFile, line, 'utf8').catch(() => {});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function escapeHtml(str) {
|
|
24
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function markdownToHtml(text) {
|
|
28
|
+
// 1. Block fences: ```[lang]\ncontent\n``` → <pre>content</pre>
|
|
29
|
+
text = text.replace(/```[\w]*\n([\s\S]*?)\n?```/g, (_, content) => {
|
|
30
|
+
return `<pre>${escapeHtml(content)}</pre>`;
|
|
31
|
+
});
|
|
32
|
+
// 2. Inline code: `content` → <code>content</code> (no newlines inside)
|
|
33
|
+
text = text.replace(/`([^`\n]+)`/g, (_, content) => {
|
|
34
|
+
return `<code>${escapeHtml(content)}</code>`;
|
|
35
|
+
});
|
|
36
|
+
return text;
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
async function sendMessage(api, chatId, text, sessionId) {
|
|
24
40
|
const MAX_TG = 4096;
|
|
25
41
|
// Telegram HTML mode does not support <br> — replace with newlines before sending
|
|
26
42
|
text = text.replace(/<br\s*\/?>/gi, '\n');
|
|
43
|
+
// Convert leftover Markdown code fences to HTML (model sometimes mixes both formats)
|
|
44
|
+
text = markdownToHtml(text);
|
|
27
45
|
const chunks = [];
|
|
28
46
|
for (let i = 0; i < text.length; i += MAX_TG) {
|
|
29
47
|
chunks.push(text.slice(i, i + MAX_TG));
|
|
@@ -154,9 +172,15 @@ export async function startTelegramChannel(config) {
|
|
|
154
172
|
}
|
|
155
173
|
}
|
|
156
174
|
|
|
175
|
+
let lastCheckpointSent = null;
|
|
157
176
|
let result;
|
|
158
177
|
try {
|
|
159
|
-
result = await handleChat(config, sessionId, userText, allAttachments)
|
|
178
|
+
result = await handleChat(config, sessionId, userText, allAttachments, async (checkpointResponse) => {
|
|
179
|
+
const text = typeof checkpointResponse === 'string' ? checkpointResponse : JSON.stringify(checkpointResponse);
|
|
180
|
+
lastCheckpointSent = text;
|
|
181
|
+
await appendTelegramChatLog(chatId, sessions[chatId] || null, 'JARVIS', text);
|
|
182
|
+
await sendMessage(api, chatId, text, sessions[chatId] || null);
|
|
183
|
+
});
|
|
160
184
|
} catch (e) {
|
|
161
185
|
console.error(`[telegram] agent error chat_id=${chatId}: ${e.message}`);
|
|
162
186
|
const errText = e.message
|
|
@@ -185,9 +209,16 @@ export async function startTelegramChannel(config) {
|
|
|
185
209
|
: result.response != null ? JSON.stringify(result.response, null, 2) : '';
|
|
186
210
|
const text = rawResponse.trim()
|
|
187
211
|
|| 'The agent encountered an error and could not produce a response. Please try again.';
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
212
|
+
// Skip sending if this response was already sent as a checkpoint update —
|
|
213
|
+
// intervention_required and zero-progress reuse the last checkpoint response
|
|
214
|
+
// as their finalResponse, which would otherwise cause a duplicate message.
|
|
215
|
+
if (text !== lastCheckpointSent) {
|
|
216
|
+
await appendTelegramChatLog(chatId, result.sessionId, 'JARVIS', text);
|
|
217
|
+
await sendMessage(api, chatId, text, result.sessionId);
|
|
218
|
+
console.log(`[telegram] response sent chat_id=${chatId} length=${text.length}`);
|
|
219
|
+
} else {
|
|
220
|
+
console.log(`[telegram] skipped duplicate final response chat_id=${chatId}`);
|
|
221
|
+
}
|
|
191
222
|
} catch (e) {
|
|
192
223
|
console.error(`[telegram] delivery error chat_id=${chatId}: ${e.message}`);
|
|
193
224
|
await api.sendMessage(chatId, 'Sorry, something went wrong sending the response. Please try again.').catch(() => {});
|
package/src/server/agent.js
CHANGED
|
@@ -618,7 +618,7 @@ export async function withSessionLock(sessionId, fn) {
|
|
|
618
618
|
* Main entry point: handles a single POST /api/chat request.
|
|
619
619
|
* Manages the handoff loop across multiple agent runs.
|
|
620
620
|
*/
|
|
621
|
-
export async function handleChat(config, requestSessionId, userMessage, attachments = []) {
|
|
621
|
+
export async function handleChat(config, requestSessionId, userMessage, attachments = [], onCheckpoint = null) {
|
|
622
622
|
const sessionId = requestSessionId || crypto.randomUUID();
|
|
623
623
|
|
|
624
624
|
// Serialize concurrent requests for the same session. Each request registers
|
|
@@ -632,7 +632,7 @@ export async function handleChat(config, requestSessionId, userMessage, attachme
|
|
|
632
632
|
await previous;
|
|
633
633
|
|
|
634
634
|
try {
|
|
635
|
-
return await _runHandleChat(config, sessionId, userMessage, attachments);
|
|
635
|
+
return await _runHandleChat(config, sessionId, userMessage, attachments, onCheckpoint);
|
|
636
636
|
} finally {
|
|
637
637
|
releaseLock();
|
|
638
638
|
// Clean up only if no one else has queued behind us
|
|
@@ -646,7 +646,7 @@ export async function handleChat(config, requestSessionId, userMessage, attachme
|
|
|
646
646
|
* The actual chat logic, extracted so handleChat can wrap it cleanly with the
|
|
647
647
|
* session lock.
|
|
648
648
|
*/
|
|
649
|
-
async function _runHandleChat(config, sessionId, userMessage, attachments = []) {
|
|
649
|
+
async function _runHandleChat(config, sessionId, userMessage, attachments = [], onCheckpoint = null) {
|
|
650
650
|
const client = createClient(config);
|
|
651
651
|
|
|
652
652
|
const systemPromptTemplate = loadSystemPrompt();
|
|
@@ -802,7 +802,9 @@ async function _runHandleChat(config, sessionId, userMessage, attachments = [])
|
|
|
802
802
|
break;
|
|
803
803
|
}
|
|
804
804
|
|
|
805
|
-
// Checkpoint reached — log this run
|
|
805
|
+
// Checkpoint reached — log this run and notify the caller (e.g. Telegram adapter)
|
|
806
|
+
// so intermediate progress is visible to the user instead of being swallowed
|
|
807
|
+
// by the handoff loop until the final response.
|
|
806
808
|
await appendLog(sessionId, {
|
|
807
809
|
iteration: run.iteration,
|
|
808
810
|
model: config.selectedModel,
|
|
@@ -812,6 +814,7 @@ async function _runHandleChat(config, sessionId, userMessage, attachments = [])
|
|
|
812
814
|
logSummary: run.logSummary,
|
|
813
815
|
status: 'checkpoint_reached',
|
|
814
816
|
});
|
|
817
|
+
if (onCheckpoint) await onCheckpoint(run.response);
|
|
815
818
|
|
|
816
819
|
// Accumulate failedApproaches from this run into session metadata so the
|
|
817
820
|
// full history of failed strategies is available across all handoff runs.
|