@ducci/jarvis 1.0.52 → 1.0.54

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ducci/jarvis",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "description": "A fully automated agent system that lives on a server.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -9,6 +9,12 @@ import chalk from 'chalk';
9
9
 
10
10
  const FORMAT_NUDGE = 'Your previous response was not valid JSON. Respond only with the required JSON object: {"response": "...", "logSummary": "..."}';
11
11
  const LOOP_DETECTION_THRESHOLD = 3;
12
+
13
+ // Strip markdown code fences (```json...``` or ```...```) that models sometimes
14
+ // wrap around JSON responses, which would otherwise cause JSON.parse to throw.
15
+ function stripCodeFence(text) {
16
+ return text.replace(/^```(?:json)?\s*\n([\s\S]*?)\n?```\s*$/, '$1').trim();
17
+ }
12
18
  const CONSECUTIVE_FAILURE_THRESHOLD = 3;
13
19
  const MAX_TOOL_RESULT = 4000;
14
20
 
@@ -396,20 +402,20 @@ export async function runAgentLoop(client, config, session, prepareMessages, usa
396
402
  if (nudgeContent.trim()) {
397
403
  content = nudgeContent;
398
404
  }
399
- parsed = JSON.parse(nudgeContent);
405
+ parsed = JSON.parse(stripCodeFence(nudgeContent));
400
406
  } catch {
401
407
  // Fall through to !parsed handler; content may now carry the nudge text
402
408
  }
403
409
  } else {
404
410
  try {
405
- parsed = JSON.parse(content);
411
+ parsed = JSON.parse(stripCodeFence(content));
406
412
  } catch {
407
413
  // Step 1: retry with fallback model
408
414
  try {
409
415
  const fallbackResult = await callModel(client, config.fallbackModel, preparedMessages, toolDefs);
410
416
  accumulateUsage(usageAccum, fallbackResult);
411
417
  const fallbackContent = fallbackResult.choices[0]?.message?.content || '';
412
- parsed = JSON.parse(fallbackContent);
418
+ parsed = JSON.parse(stripCodeFence(fallbackContent));
413
419
  content = fallbackContent;
414
420
  } catch {
415
421
  // Step 2: nudge retry via both models
@@ -418,7 +424,7 @@ export async function runAgentLoop(client, config, session, prepareMessages, usa
418
424
  const nudgeResult = await callModelWithFallback(client, config, nudgeMessages, toolDefs);
419
425
  accumulateUsage(usageAccum, nudgeResult);
420
426
  const nudgeContent = nudgeResult.choices[0]?.message?.content || '';
421
- parsed = JSON.parse(nudgeContent);
427
+ parsed = JSON.parse(stripCodeFence(nudgeContent));
422
428
  content = nudgeContent;
423
429
  } catch {
424
430
  // Give up
@@ -489,14 +495,14 @@ export async function runAgentLoop(client, config, session, prepareMessages, usa
489
495
 
490
496
  // Try JSON parse; if it fails, nudge retry (Layer 2)
491
497
  try {
492
- parsedWrapUp = JSON.parse(wrapUpContent);
498
+ parsedWrapUp = JSON.parse(stripCodeFence(wrapUpContent));
493
499
  } catch {
494
500
  try {
495
501
  const nudgeMessages = [...wrapUpMessages, { role: 'user', content: FORMAT_NUDGE }];
496
502
  const nudgeResult = await callModelWithFallback(client, config, nudgeMessages, []);
497
503
  accumulateUsage(usageAccum, nudgeResult);
498
504
  const nudgeContent = nudgeResult.choices[0]?.message?.content || '';
499
- parsedWrapUp = JSON.parse(nudgeContent);
505
+ parsedWrapUp = JSON.parse(stripCodeFence(nudgeContent));
500
506
  wrapUpContent = nudgeContent;
501
507
  } catch {
502
508
  // Layer 3: use raw text as best-effort response below
@@ -38,7 +38,7 @@ const SEED_TOOLS = {
38
38
  },
39
39
  },
40
40
  code: `
41
- const targetPath = path.resolve(args.path);
41
+ const targetPath = path.resolve(args.path.replace(/^~(?=\/|$)/, require('os').homedir()));
42
42
  const raw = await fs.promises.readFile(targetPath, 'utf8');
43
43
  const lines = raw.split('\\n');
44
44
  const offset = args.offset ? args.offset - 1 : 0;
@@ -308,7 +308,7 @@ const SEED_TOOLS = {
308
308
  },
309
309
  },
310
310
  code: `
311
- const targetPath = path.resolve(args.path);
311
+ const targetPath = path.resolve(args.path.replace(/^~(?=\/|$)/, require('os').homedir()));
312
312
  await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
313
313
  await fs.promises.writeFile(targetPath, args.content, 'utf8');
314
314
  if (args.mode) {
@@ -345,7 +345,7 @@ const SEED_TOOLS = {
345
345
  },
346
346
  },
347
347
  code: `
348
- const targetPath = path.resolve(args.path);
348
+ const targetPath = path.resolve(args.path.replace(/^~(?=\/|$)/, require('os').homedir()));
349
349
  const content = await fs.promises.readFile(targetPath, 'utf8');
350
350
  const count = content.split(args.old_string).length - 1;
351
351
  if (count === 0) {