@ducci/jarvis 1.0.7 → 1.0.8

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.7",
3
+ "version": "1.0.8",
4
4
  "description": "A fully automated agent system that lives on a server.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -4,6 +4,7 @@ import { loadSystemPrompt, resolveSystemPrompt } from './config.js';
4
4
  import { loadSession, saveSession, createSession } from './sessions.js';
5
5
  import { loadTools, getToolDefinitions, executeTool } from './tools.js';
6
6
  import { appendLog } from './logging.js';
7
+ import chalk from 'chalk';
7
8
 
8
9
  const WRAP_UP_NOTE = `[System: You have reached the iteration limit. This is your final response for this run.
9
10
  Respond with your normal JSON, but add a checkpoint field:
@@ -27,17 +28,33 @@ async function callModel(client, model, messages, tools) {
27
28
  return await client.chat.completions.create(params);
28
29
  }
29
30
 
31
+ function extractApiError(err, model) {
32
+ return {
33
+ model,
34
+ httpStatus: err?.status ?? null,
35
+ message: err?.message ?? String(err),
36
+ body: err?.error ?? null,
37
+ };
38
+ }
39
+
30
40
  async function callModelWithFallback(client, config, messages, tools) {
41
+ let primaryErr = null;
31
42
  try {
32
43
  return await callModel(client, config.selectedModel, messages, tools);
33
- } catch (primaryErr) {
34
- try {
35
- return await callModel(client, config.fallbackModel, messages, tools);
36
- } catch (fallbackErr) {
37
- throw new Error(
38
- `Both primary (${config.selectedModel}) and fallback (${config.fallbackModel}) models failed. Last error: ${fallbackErr.message}`
39
- );
40
- }
44
+ } catch (err) {
45
+ primaryErr = err;
46
+ }
47
+ try {
48
+ return await callModel(client, config.fallbackModel, messages, tools);
49
+ } catch (fallbackErr) {
50
+ const combined = new Error(
51
+ `Both primary (${config.selectedModel}) and fallback (${config.fallbackModel}) models failed. Last error: ${fallbackErr.message}`
52
+ );
53
+ combined.apiErrors = {
54
+ primary: extractApiError(primaryErr, config.selectedModel),
55
+ fallback: extractApiError(fallbackErr, config.fallbackModel),
56
+ };
57
+ throw combined;
41
58
  }
42
59
  }
43
60
 
@@ -57,8 +74,9 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
57
74
  iteration++;
58
75
 
59
76
  let modelResult;
77
+ const preparedMessages = prepareMessages(session.messages);
60
78
  try {
61
- modelResult = await callModelWithFallback(client, config, prepareMessages(session.messages), toolDefs);
79
+ modelResult = await callModelWithFallback(client, config, preparedMessages, toolDefs);
62
80
  } catch (e) {
63
81
  return {
64
82
  iteration,
@@ -67,6 +85,8 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
67
85
  status: 'model_error',
68
86
  runToolCalls,
69
87
  checkpoint: null,
88
+ errorDetail: e.apiErrors ?? { message: e.message },
89
+ contextInfo: { messageCount: preparedMessages.length },
70
90
  };
71
91
  }
72
92
 
@@ -123,6 +143,7 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
123
143
  response = content;
124
144
  logSummary = 'Model returned non-JSON final response.';
125
145
  status = 'format_error';
146
+ return { iteration, response, logSummary, status, runToolCalls, checkpoint: null, rawResponse: content };
126
147
  }
127
148
 
128
149
  done = true;
@@ -147,6 +168,8 @@ async function runAgentLoop(client, config, session, tools, toolDefs, prepareMes
147
168
  status: 'model_error',
148
169
  runToolCalls,
149
170
  checkpoint: null,
171
+ errorDetail: e.apiErrors ?? { message: e.message },
172
+ contextInfo: { messageCount: wrapUpMessages.length },
150
173
  };
151
174
  }
152
175
 
@@ -230,7 +253,7 @@ export async function handleChat(config, requestSessionId, userMessage) {
230
253
  finalLogSummary = run.logSummary;
231
254
  finalStatus = run.status;
232
255
 
233
- appendLog(sessionId, {
256
+ const logEntry = {
234
257
  iteration: run.iteration,
235
258
  model: config.selectedModel,
236
259
  userInput: userMessage,
@@ -238,7 +261,11 @@ export async function handleChat(config, requestSessionId, userMessage) {
238
261
  response: finalResponse,
239
262
  logSummary: finalLogSummary,
240
263
  status: finalStatus,
241
- });
264
+ };
265
+ if (run.errorDetail) logEntry.errorDetail = run.errorDetail;
266
+ if (run.contextInfo) logEntry.contextInfo = run.contextInfo;
267
+ if (run.rawResponse) logEntry.rawResponse = run.rawResponse;
268
+ appendLog(sessionId, logEntry);
242
269
  break;
243
270
  }
244
271
 
@@ -278,6 +305,8 @@ export async function handleChat(config, requestSessionId, userMessage) {
278
305
 
279
306
  saveSession(sessionId, session);
280
307
 
308
+ console.log(`${chalk.magenta('<<<')} ${chalk.bold('Final Response')} [SID: ${chalk.dim(sessionId.slice(0, 8))}] ${chalk.italic(finalLogSummary)}`);
309
+
281
310
  return {
282
311
  sessionId,
283
312
  response: finalResponse,
package/src/server/app.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import express from 'express';
2
2
  import path from 'path';
3
+ import chalk from 'chalk';
3
4
  import { fileURLToPath } from 'url';
4
5
  import { realpathSync } from 'fs';
5
6
  import { loadConfig, ensureDirectories } from './config.js';
@@ -12,6 +13,15 @@ const __dirname = path.dirname(__filename);
12
13
  const app = express();
13
14
  app.use(express.json());
14
15
 
16
+ // Request logger
17
+ app.use((req, res, next) => {
18
+ if (req.path === '/api/chat' && req.method === 'POST') {
19
+ const sid = req.body.sessionId || 'new';
20
+ console.log(`\n${chalk.magenta('>>>')} ${chalk.bold('Incoming Chat')} [SID: ${chalk.dim(sid.slice(0, 8))}]`);
21
+ }
22
+ next();
23
+ });
24
+
15
25
  // Serve the built UI as static files
16
26
  const uiDist = path.join(__dirname, '..', '..', 'ui', 'dist');
17
27
  app.use(express.static(uiDist));
@@ -1,9 +1,20 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import chalk from 'chalk';
3
4
  import { PATHS } from './config.js';
4
5
 
5
6
  export function appendLog(sessionId, entry) {
6
7
  const logFile = path.join(PATHS.logsDir, `session-${sessionId}.jsonl`);
7
8
  const line = JSON.stringify({ ts: new Date().toISOString(), sessionId, ...entry }) + '\n';
8
9
  fs.appendFileSync(logFile, line, 'utf8');
10
+
11
+ // Console output for better visibility
12
+ const statusColor = entry.status === 'ok' ? chalk.green : chalk.red;
13
+ console.log(
14
+ `[${chalk.dim(new Date().toLocaleTimeString())}] ` +
15
+ `${chalk.blue('Session')}: ${chalk.dim(sessionId.slice(0, 8))} | ` +
16
+ `${chalk.yellow('Iter')}: ${entry.iteration} | ` +
17
+ `${chalk.cyan('Status')}: ${statusColor(entry.status)} | ` +
18
+ `${entry.logSummary || '(no summary)'}`
19
+ );
9
20
  }