@ducci/jarvis 1.0.17 → 1.0.19
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 +8 -0
- package/package.json +1 -1
- package/src/channels/telegram/index.js +26 -10
- package/src/server/tools.js +41 -4
package/docs/system-prompt.md
CHANGED
|
@@ -53,6 +53,14 @@ The `exec` tool runs real shell commands on the server. Use it responsibly:
|
|
|
53
53
|
- **Prefer targeted reads.** Use `grep`, `head`, or `tail` instead of `cat` on files you haven't seen before. Large file output is truncated anyway — a targeted command gives you better signal.
|
|
54
54
|
- **Avoid commands with unbounded runtime.** If a command could run indefinitely or scan an unknown-size tree, scope it first.
|
|
55
55
|
|
|
56
|
+
## Tool Creation
|
|
57
|
+
|
|
58
|
+
When building a custom tool with `save_tool`:
|
|
59
|
+
|
|
60
|
+
- **Prefer npm packages** over reimplementing functionality from scratch. If a well-known package exists for the task (e.g. an API SDK, a parser, a utility library), use it.
|
|
61
|
+
- **Installing an npm package**: use the `npm_install` tool — it handles the correct install directory automatically. Then create the tool with `save_tool`. The tool code can `require('<package-name>')` directly.
|
|
62
|
+
- **Available bindings in tool code**: `args`, `fs`, `path`, `process`, `require`, `__jarvisDir` (absolute path to the jarvis server directory).
|
|
63
|
+
|
|
56
64
|
## logSummary Guidelines
|
|
57
65
|
|
|
58
66
|
The `logSummary` is written for a human observer, not for the user. It must:
|
package/package.json
CHANGED
|
@@ -45,20 +45,36 @@ export async function startTelegramChannel(config) {
|
|
|
45
45
|
ctx.api.sendChatAction(chatId, 'typing').catch(() => {});
|
|
46
46
|
}, 4000);
|
|
47
47
|
|
|
48
|
+
let result;
|
|
48
49
|
try {
|
|
49
|
-
|
|
50
|
+
result = await handleChat(config, sessionId, ctx.message.text);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.error(`[telegram] agent error chat_id=${chatId}: ${e.message}`);
|
|
53
|
+
await ctx.reply('Sorry, something went wrong. Please try again.');
|
|
54
|
+
clearInterval(typingInterval);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
// Persist new session mapping on first message
|
|
59
|
+
if (!sessions[chatId]) {
|
|
60
|
+
sessions[chatId] = result.sessionId;
|
|
61
|
+
save(sessions);
|
|
62
|
+
console.log(`[telegram] session created sessionId=${result.sessionId.slice(0, 8)}`);
|
|
63
|
+
}
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
try {
|
|
66
|
+
const MAX_TG = 4096;
|
|
67
|
+
const text = result.response;
|
|
68
|
+
if (text.length <= MAX_TG) {
|
|
69
|
+
await ctx.reply(text);
|
|
70
|
+
} else {
|
|
71
|
+
for (let i = 0; i < text.length; i += MAX_TG) {
|
|
72
|
+
await ctx.reply(text.slice(i, i + MAX_TG));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
console.log(`[telegram] response sent chat_id=${chatId} length=${text.length}`);
|
|
60
76
|
} catch (e) {
|
|
61
|
-
console.error(`[telegram] error chat_id=${chatId}: ${e.message}`);
|
|
77
|
+
console.error(`[telegram] delivery error chat_id=${chatId} length=${result.response.length}: ${e.message}`);
|
|
62
78
|
await ctx.reply('Sorry, something went wrong. Please try again.');
|
|
63
79
|
} finally {
|
|
64
80
|
clearInterval(typingInterval);
|
package/src/server/tools.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import { PATHS } from './config.js';
|
|
5
6
|
|
|
6
7
|
const _require = createRequire(import.meta.url);
|
|
8
|
+
const __jarvisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
7
9
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
8
10
|
|
|
9
11
|
const TOOL_TIMEOUT_MS = 60_000;
|
|
@@ -122,7 +124,7 @@ const SEED_TOOLS = {
|
|
|
122
124
|
type: 'function',
|
|
123
125
|
function: {
|
|
124
126
|
name: 'save_tool',
|
|
125
|
-
description: 'Create or update a custom tool and make it available immediately in this session. Use this to build reusable JS tools for tasks you repeat. The tool code runs in Node.js and has access to: args, fs, path, process, require.',
|
|
127
|
+
description: 'Create or update a custom tool and make it available immediately in this session. Use this to build reusable JS tools for tasks you repeat. The tool code runs in Node.js and has access to: args, fs, path, process, require, __jarvisDir.',
|
|
126
128
|
parameters: {
|
|
127
129
|
type: 'object',
|
|
128
130
|
properties: {
|
|
@@ -140,7 +142,7 @@ const SEED_TOOLS = {
|
|
|
140
142
|
},
|
|
141
143
|
code: {
|
|
142
144
|
type: 'string',
|
|
143
|
-
description: 'The body of an async function. Must end with a return statement — the returned value becomes the tool result. Available bindings: args (your tool parameters), fs (node:fs), path (node:path), process, require. Do NOT wrap in a function declaration. Example: const raw = await fs.promises.readFile(args.filePath, "utf8"); const data = JSON.parse(raw); return { count: data.length, first: data[0] };',
|
|
145
|
+
description: 'The body of an async function. Must end with a return statement — the returned value becomes the tool result. Available bindings: args (your tool parameters), fs (node:fs), path (node:path), process, require, __jarvisDir (absolute path to the jarvis server directory — use path.resolve(__jarvisDir, "../..") to get the project root for npm installs). Do NOT wrap in a function declaration. Example: const raw = await fs.promises.readFile(args.filePath, "utf8"); const data = JSON.parse(raw); return { count: data.length, first: data[0] };',
|
|
144
146
|
},
|
|
145
147
|
},
|
|
146
148
|
required: ['name', 'description', 'parameters', 'code'],
|
|
@@ -164,6 +166,41 @@ const SEED_TOOLS = {
|
|
|
164
166
|
},
|
|
165
167
|
code: `const toolsFile = path.join(process.env.HOME, '.jarvis/data/tools/tools.json'); const raw = await fs.promises.readFile(toolsFile, 'utf8').catch(() => '{}'); const tools = JSON.parse(raw); const list = Object.entries(tools).map(([name, t]) => ({ name, description: t.definition.function.description })); return { status: 'ok', tools: list };`,
|
|
166
168
|
},
|
|
169
|
+
npm_install: {
|
|
170
|
+
definition: {
|
|
171
|
+
type: 'function',
|
|
172
|
+
function: {
|
|
173
|
+
name: 'npm_install',
|
|
174
|
+
description: 'Install an npm package into the jarvis project so it can be used in tool code via require(). Always use this instead of exec to install packages.',
|
|
175
|
+
parameters: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {
|
|
178
|
+
packageName: {
|
|
179
|
+
type: 'string',
|
|
180
|
+
description: 'The npm package name to install, e.g. "@perplexity-ai/sdk" or "axios".',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
required: ['packageName'],
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
code: `
|
|
188
|
+
const { exec } = require('child_process');
|
|
189
|
+
const { promisify } = require('util');
|
|
190
|
+
const execAsync = promisify(exec);
|
|
191
|
+
const projectRoot = path.resolve(__jarvisDir, '../..');
|
|
192
|
+
try {
|
|
193
|
+
const { stdout, stderr } = await execAsync('npm install ' + args.packageName, {
|
|
194
|
+
cwd: projectRoot,
|
|
195
|
+
encoding: 'utf8',
|
|
196
|
+
timeout: 60000,
|
|
197
|
+
});
|
|
198
|
+
return { status: 'ok', packageName: args.packageName, stdout, stderr };
|
|
199
|
+
} catch (e) {
|
|
200
|
+
return { status: 'error', packageName: args.packageName, stderr: e.stderr || e.message };
|
|
201
|
+
}
|
|
202
|
+
`,
|
|
203
|
+
},
|
|
167
204
|
get_recent_sessions: {
|
|
168
205
|
definition: {
|
|
169
206
|
type: 'function',
|
|
@@ -255,7 +292,7 @@ export async function executeTool(tools, name, toolArgs) {
|
|
|
255
292
|
throw new Error(`Unknown tool: ${name}`);
|
|
256
293
|
}
|
|
257
294
|
|
|
258
|
-
const fn = new AsyncFunction('args', 'fs', 'path', 'process', 'require', tool.code);
|
|
295
|
+
const fn = new AsyncFunction('args', 'fs', 'path', 'process', 'require', '__jarvisDir', tool.code);
|
|
259
296
|
|
|
260
297
|
const timeout = new Promise((_, reject) =>
|
|
261
298
|
setTimeout(
|
|
@@ -264,5 +301,5 @@ export async function executeTool(tools, name, toolArgs) {
|
|
|
264
301
|
)
|
|
265
302
|
);
|
|
266
303
|
|
|
267
|
-
return await Promise.race([fn(toolArgs, fs, path, process, _require), timeout]);
|
|
304
|
+
return await Promise.race([fn(toolArgs, fs, path, process, _require, __jarvisDir), timeout]);
|
|
268
305
|
}
|