aiden-runtime 3.18.0 → 3.19.4

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.
@@ -88,7 +88,8 @@ const multiGoalValidator_1 = require("../core/multiGoalValidator");
88
88
  const toolRegistry_2 = require("../core/toolRegistry");
89
89
  const reactLoop_1 = require("../core/reactLoop");
90
90
  const scheduler_1 = require("../core/scheduler");
91
- const aidenPersonality_1 = require("../core/aidenPersonality");
91
+ const protectedContext_1 = require("../core/protectedContext");
92
+ const contextHandoff_1 = require("../core/contextHandoff");
92
93
  const voiceInput_1 = require("../core/voiceInput");
93
94
  const voiceOutput_1 = require("../core/voiceOutput");
94
95
  const planTool_1 = require("../core/planTool");
@@ -122,8 +123,10 @@ const skillWriter_1 = require("../core/skillWriter");
122
123
  const skillLibrary_1 = require("../core/skillLibrary");
123
124
  const costTracker_1 = require("../core/costTracker");
124
125
  const sessionMemory_1 = require("../core/sessionMemory");
126
+ const diagnosticError_1 = require("../core/diagnosticError");
125
127
  const memoryExtractor_1 = require("../core/memoryExtractor");
126
128
  const pluginLoader_1 = require("../core/pluginLoader");
129
+ const commandCatalog = __importStar(require("../cli/commandCatalog"));
127
130
  const permissionSystem_1 = require("../core/permissionSystem");
128
131
  const aidenIdentity_1 = require("../core/aidenIdentity");
129
132
  const eventBus_1 = require("../core/eventBus");
@@ -170,46 +173,41 @@ const INSTANT_ACTIONS = [
170
173
  /^capture\s+(?:the\s+)?screen\s*$/i,
171
174
  ],
172
175
  action: async () => {
173
- try {
174
- const result = await (0, toolRegistry_1.executeTool)('screenshot', {});
175
- if (result.success && result.output)
176
- return result.output;
177
- }
178
- catch { }
179
- return 'Screenshot taken.';
176
+ const result = await (0, toolRegistry_1.executeTool)('screenshot', {});
177
+ if (result.success)
178
+ return result.output || 'Screenshot taken.';
179
+ return `Couldn't take screenshot: ${result.error || 'tool returned no diagnostic'}`;
180
180
  },
181
181
  },
182
182
  // 10. Volume Up
183
183
  {
184
184
  patterns: [/^(?:turn\s+(?:the\s+)?)?volume\s+up\s*$/i],
185
185
  action: async () => {
186
- try {
187
- await (0, toolRegistry_1.executeTool)('shell_exec', { command: 'powershell -c “(New-Object -com WScript.Shell).SendKeys([char]175)”' });
188
- }
189
- catch { }
190
- return 'Volume up.';
186
+ const result = await (0, toolRegistry_1.executeTool)('system_volume', { action: 'up' });
187
+ if (result.success)
188
+ return result.output || 'Volume up.';
189
+ return `Couldn't change volume: ${result.error || 'tool returned no diagnostic'}`;
191
190
  },
192
191
  },
193
192
  // 11. Volume Down
194
193
  {
195
194
  patterns: [/^(?:turn\s+(?:the\s+)?)?volume\s+down\s*$/i],
196
195
  action: async () => {
197
- try {
198
- await (0, toolRegistry_1.executeTool)('shell_exec', { command: 'powershell -c “(New-Object -com WScript.Shell).SendKeys([char]174)”' });
199
- }
200
- catch { }
201
- return 'Volume down.';
196
+ const result = await (0, toolRegistry_1.executeTool)('system_volume', { action: 'down' });
197
+ if (result.success)
198
+ return result.output || 'Volume down.';
199
+ return `Couldn't change volume: ${result.error || 'tool returned no diagnostic'}`;
202
200
  },
203
201
  },
204
202
  // 12. Mute / Unmute
205
203
  {
206
204
  patterns: [/^(?:toggle\s+)?mute\s*$/i, /^unmute\s*$/i],
207
- action: async () => {
208
- try {
209
- await (0, toolRegistry_1.executeTool)('shell_exec', { command: 'powershell -c “(New-Object -com WScript.Shell).SendKeys([char]173)”' });
210
- }
211
- catch { }
212
- return 'Toggled mute.';
205
+ action: async (_match, message) => {
206
+ const muteAction = /^unmute/i.test(message ?? '') ? 'unmute' : 'mute';
207
+ const result = await (0, toolRegistry_1.executeTool)('system_volume', { action: muteAction });
208
+ if (result.success)
209
+ return result.output || (muteAction === 'mute' ? 'Muted.' : 'Unmuted.');
210
+ return `Couldn't ${muteAction}: ${result.error || 'tool returned no diagnostic'}`;
213
211
  },
214
212
  },
215
213
  // 13. Set Timer
@@ -253,11 +251,10 @@ const INSTANT_ACTIONS = [
253
251
  {
254
252
  patterns: [/^lock\s+(?:the\s+)?(?:screen|pc|computer|workstation)\s*$/i],
255
253
  action: async () => {
256
- try {
257
- await (0, toolRegistry_1.executeTool)('shell_exec', { command: 'rundll32.exe user32.dll,LockWorkStation' });
258
- }
259
- catch { }
260
- return 'Locking screen...';
254
+ const result = await (0, toolRegistry_1.executeTool)('shell_exec', { command: 'rundll32.exe user32.dll,LockWorkStation' });
255
+ if (result.success)
256
+ return 'Locking screen...';
257
+ return `Couldn't lock screen: ${result.error || 'tool returned no diagnostic'}`;
261
258
  },
262
259
  },
263
260
  ];
@@ -361,6 +358,10 @@ function handleChatError(err, apiName, send) {
361
358
  }
362
359
  // Workspace root — AIDEN_USER_DATA in packaged Electron, cwd in dev
363
360
  const WORKSPACE_ROOT = process.env.AIDEN_USER_DATA || process.cwd();
361
+ // Per-session soul hash for Option-B protected-context injection.
362
+ // First turn: undefined → full SOUL inject. Subsequent turns: compare → emit
363
+ // reference line when unchanged, re-inject when SOUL.md edited on disk.
364
+ const soulHashBySession = new Map();
364
365
  // ── Workspace bootstrap — create default dirs + files on every boot ──────────
365
366
  function initWorkspaceDefaults() {
366
367
  const dirs = [
@@ -924,7 +925,10 @@ function createApiServer() {
924
925
  return;
925
926
  }
926
927
  }
927
- // ── Instant Actions — 15 direct OS commands, zero LLM overhead ─────────────
928
+ // ── Instant Actions — 7 direct OS commands, zero LLM overhead ──────────────
929
+ // NOTE: was 15 prior to v3.19 P3. Entries 1-8 (open/close/launch fake actions)
930
+ // removed entirely. Entries 9-15 (screenshot, volume, mute, timer, sysinfo,
931
+ // lock) retained with handlers rewritten to use real executeTool() calls.
928
932
  for (const ia of INSTANT_ACTIONS) {
929
933
  for (const pat of ia.patterns) {
930
934
  const m = message.match(pat);
@@ -1802,6 +1806,7 @@ function createApiServer() {
1802
1806
  { id: 'groq', label: 'Groq', subtitle: 'Free tier · llama3.3:70b · blazing fast', url: 'https://console.groq.com', models: ['llama-3.3-70b-versatile', 'llama-3.1-70b-versatile', 'mixtral-8x7b-32768'] },
1803
1807
  { id: 'openrouter', label: 'OpenRouter', subtitle: 'Access 200+ models · pay per use', url: 'https://openrouter.ai/keys', models: ['meta-llama/llama-3.3-70b-instruct', 'anthropic/claude-3.5-sonnet', 'openai/gpt-4o'] },
1804
1808
  { id: 'gemini', label: 'Gemini', subtitle: 'Free tier available · fast', url: 'https://aistudio.google.com/app/apikey', models: ['gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-2.0-flash-exp'] },
1809
+ { id: 'mistral', label: 'Mistral AI', subtitle: 'Mistral Large/Small · Codestral', url: 'https://console.mistral.ai/api-keys', models: ['mistral-large-latest', 'mistral-small-latest', 'codestral-latest'] },
1805
1810
  { id: 'cloudflare', label: 'Cloudflare AI', subtitle: '60+ models · free tier · edge inference', url: 'https://dash.cloudflare.com/profile/api-tokens', models: ['accountId|@cf/meta/llama-3.1-8b-instruct'] },
1806
1811
  { id: 'github', label: 'GitHub Models', subtitle: 'GPT-4o · free for GitHub users', url: 'https://github.com/marketplace/models', models: ['gpt-4o-mini', 'gpt-4o'] },
1807
1812
  ];
@@ -2639,7 +2644,7 @@ function createApiServer() {
2639
2644
  app.post('/api/plugins/reload', requireLocalhost, async (_req, res) => {
2640
2645
  try {
2641
2646
  const dir = path.join(process.cwd(), 'workspace', 'plugins');
2642
- await (0, pluginLoader_1.reloadPlugins)(dir);
2647
+ await (0, pluginLoader_1.reloadPlugins)(dir, { commandCatalog });
2643
2648
  res.json({ ok: true, plugins: (0, pluginLoader_1.listFlatPlugins)() });
2644
2649
  }
2645
2650
  catch (e) {
@@ -2849,6 +2854,18 @@ function createApiServer() {
2849
2854
  error = `${r.status}: ${await r.text()}`;
2850
2855
  break;
2851
2856
  }
2857
+ case 'mistral': {
2858
+ const r = await fetch('https://api.mistral.ai/v1/chat/completions', {
2859
+ method: 'POST',
2860
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
2861
+ body: JSON.stringify({ model: testModel, messages: testMessages, max_tokens: 5 }),
2862
+ signal: AbortSignal.timeout(8000),
2863
+ });
2864
+ valid = r.ok;
2865
+ if (!r.ok)
2866
+ error = `${r.status}: ${await r.text()}`;
2867
+ break;
2868
+ }
2852
2869
  case 'nvidia': {
2853
2870
  const r = await fetch('https://integrate.api.nvidia.com/v1/chat/completions', {
2854
2871
  method: 'POST',
@@ -2929,6 +2946,15 @@ function createApiServer() {
2929
2946
  });
2930
2947
  return res.json({ valid: r.ok, status: r.status, provider: 'groq' });
2931
2948
  }
2949
+ if (provider === 'mistral') {
2950
+ const r = await fetch('https://api.mistral.ai/v1/chat/completions', {
2951
+ method: 'POST',
2952
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` },
2953
+ body: JSON.stringify({ model: 'mistral-large-latest', messages: [{ role: 'user', content: 'hi' }], max_tokens: 5 }),
2954
+ signal: AbortSignal.timeout(8000),
2955
+ });
2956
+ return res.json({ valid: r.ok, status: r.status, provider: 'mistral' });
2957
+ }
2932
2958
  if (provider === 'ollama') {
2933
2959
  const ollamaBase = (process.env.OLLAMA_HOST ?? 'http://127.0.0.1:11434').replace(/\/$/, '');
2934
2960
  const r = await fetch(`${ollamaBase}/api/tags`, { signal: AbortSignal.timeout(3000) });
@@ -3073,9 +3099,11 @@ function createApiServer() {
3073
3099
  });
3074
3100
  // GET /api/tools — list all built-in + plugin-registered tools
3075
3101
  app.get('/api/tools', (_req, res) => {
3076
- const { TOOLS, TOOL_DESCRIPTIONS, getExternalToolsMeta } = require('../core/toolRegistry');
3077
- const names = Object.keys(TOOLS);
3102
+ const { TOOL_DESCRIPTIONS, getExternalToolsMeta } = require('../core/toolRegistry');
3103
+ // v3.19 Phase 1: use TOOL_DESCRIPTIONS keys (71 user-facing) instead of TOOLS handler
3104
+ // keys (79 = 77 registry + 2 legacy stubs) so banner count reflects real tool count.
3078
3105
  const descs = TOOL_DESCRIPTIONS || {};
3106
+ const names = Object.keys(descs);
3079
3107
  const extMeta = getExternalToolsMeta();
3080
3108
  const coreTools = names.map(name => ({ name, description: descs[name] || '', source: 'core' }));
3081
3109
  const extTools = Object.entries(extMeta).map(([name, m]) => ({
@@ -5603,6 +5631,7 @@ function getDefaultModel(provider) {
5603
5631
  gemini: 'gemini-1.5-flash',
5604
5632
  cerebras: 'llama3.1-8b',
5605
5633
  nvidia: 'meta/llama-3.3-70b-instruct',
5634
+ mistral: 'mistral-large-latest',
5606
5635
  };
5607
5636
  return defaults[provider] || 'llama-3.3-70b-versatile';
5608
5637
  }
@@ -5752,6 +5781,16 @@ function startApiServer(portArg) {
5752
5781
  console.log('[Startup] SOUL length:', _soulLen, 'chars');
5753
5782
  console.log('[Startup] Tool count:', Object.keys(toolRegistry_2.TOOL_DESCRIPTIONS).length);
5754
5783
  }
5784
+ // v3.19 Phase 1 Commit 7: throw-mode — re-throw so server FAILS to start on drift
5785
+ try {
5786
+ const { validateRegistry } = require('../core/registryValidator');
5787
+ validateRegistry();
5788
+ }
5789
+ catch (e) {
5790
+ console.error('[Startup] FATAL — registry invariant violated. Fix before deploying:');
5791
+ console.error(e.message);
5792
+ process.exit(1);
5793
+ }
5755
5794
  // ── Startup health check ─────────────────────────────────────
5756
5795
  try {
5757
5796
  startupCheck();
@@ -5857,8 +5896,9 @@ function startApiServer(portArg) {
5857
5896
  console.error('[Startup] setupHttpKeepalive failed:', e.message);
5858
5897
  }
5859
5898
  // Load plugins from workspace/plugins/*.js (unified flat format)
5899
+ // Pass commandCatalog so plugins can register slash commands at load time.
5860
5900
  const flatPluginDir = path.join(process.cwd(), 'workspace', 'plugins');
5861
- (0, pluginLoader_1.loadPlugins)(flatPluginDir).catch(e => console.error('[PluginLoader] Load failed:', e.message));
5901
+ (0, pluginLoader_1.loadPlugins)(flatPluginDir, { commandCatalog }).catch(e => console.error('[PluginLoader] Load failed:', e.message));
5862
5902
  // Start background license refresh (12-hour interval, silent)
5863
5903
  (0, licenseManager_1.startLicenseRefresh)();
5864
5904
  // Log provider chain before listening so it's visible in startup log
@@ -6176,6 +6216,23 @@ async function start(opts) {
6176
6216
  // ── Provider racing helpers ─────────────────────────────────
6177
6217
  // fetchProviderResponse: fires a single non-streaming request to a provider.
6178
6218
  // raceProviders: fires top-2 simultaneously, returns the fastest valid response.
6219
+ function extractChatMessageContent(content) {
6220
+ if (typeof content === 'string')
6221
+ return content;
6222
+ if (!Array.isArray(content))
6223
+ return '';
6224
+ return content
6225
+ .map((part) => {
6226
+ if (typeof part === 'string')
6227
+ return part;
6228
+ if (part && typeof part === 'object' && 'text' in part) {
6229
+ const text = part.text;
6230
+ return typeof text === 'string' ? text : '';
6231
+ }
6232
+ return '';
6233
+ })
6234
+ .join('');
6235
+ }
6179
6236
  async function fetchProviderResponse(api, messages, signal) {
6180
6237
  const key = api.key.startsWith('env:')
6181
6238
  ? (process.env[api.key.replace('env:', '')] || '')
@@ -6192,7 +6249,11 @@ async function fetchProviderResponse(api, messages, signal) {
6192
6249
  if (!resp.ok)
6193
6250
  throw new Error(`Gemini ${resp.status}`);
6194
6251
  const d = await resp.json();
6195
- return { text: d?.choices?.[0]?.message?.content || '', apiName: api.name, model };
6252
+ return {
6253
+ text: extractChatMessageContent(d?.choices?.[0]?.message?.content),
6254
+ apiName: api.name,
6255
+ model,
6256
+ };
6196
6257
  }
6197
6258
  else if (providerType === 'ollama') {
6198
6259
  const resp = await fetch('http://localhost:11434/api/chat', {
@@ -6223,7 +6284,11 @@ async function fetchProviderResponse(api, messages, signal) {
6223
6284
  if (!resp.ok)
6224
6285
  throw new Error(`custom:${api.name} ${resp.status}`);
6225
6286
  const d = await resp.json();
6226
- return { text: d?.choices?.[0]?.message?.content || '', apiName: api.name, model };
6287
+ return {
6288
+ text: extractChatMessageContent(d?.choices?.[0]?.message?.content),
6289
+ apiName: api.name,
6290
+ model,
6291
+ };
6227
6292
  }
6228
6293
  else {
6229
6294
  const COMPAT_ENDPOINTS = {
@@ -6234,6 +6299,7 @@ async function fetchProviderResponse(api, messages, signal) {
6234
6299
  nvidia: 'https://integrate.api.nvidia.com/v1/chat/completions',
6235
6300
  github: 'https://models.inference.ai.azure.com/chat/completions',
6236
6301
  boa: 'https://api.bayofassets.com/v1/chat/completions',
6302
+ mistral: 'https://api.mistral.ai/v1/chat/completions',
6237
6303
  };
6238
6304
  const endpoint = COMPAT_ENDPOINTS[providerType] ?? COMPAT_ENDPOINTS['groq'];
6239
6305
  const resp = await fetch(endpoint, {
@@ -6249,7 +6315,11 @@ async function fetchProviderResponse(api, messages, signal) {
6249
6315
  if (!resp.ok)
6250
6316
  throw new Error(`${providerType} ${resp.status}`);
6251
6317
  const d = await resp.json();
6252
- return { text: d?.choices?.[0]?.message?.content || '', apiName: api.name, model };
6318
+ return {
6319
+ text: extractChatMessageContent(d?.choices?.[0]?.message?.content),
6320
+ apiName: api.name,
6321
+ model,
6322
+ };
6253
6323
  }
6254
6324
  }
6255
6325
  async function raceProviders(messages, topN = 2) {
@@ -6335,6 +6405,7 @@ async function* streamTokens(providerType, apiKey, model, messages, opts = {}) {
6335
6405
  openai: 'https://api.openai.com/v1/chat/completions',
6336
6406
  boa: 'https://api.bayofassets.com/v1/chat/completions',
6337
6407
  gemini: 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions',
6408
+ mistral: 'https://api.mistral.ai/v1/chat/completions',
6338
6409
  };
6339
6410
  // Shared tool-call buffering helper
6340
6411
  let toolBuf = '';
@@ -6452,23 +6523,7 @@ async function* streamTokens(providerType, apiKey, model, messages, opts = {}) {
6452
6523
  }
6453
6524
  // ── Pure-chat streaming helper (no planner, no tools) ─────────
6454
6525
  async function streamChat(message, history, userName, _provider, model, apiName, send, sessionId) {
6455
- // ── Sprint 1: First Message WOW — silent system context gathering ───────────────────
6456
6526
  const isFirstMessage = history.length === 0;
6457
- let firstMessageContext = '';
6458
- if (isFirstMessage) {
6459
- try {
6460
- const [sysResult, windowsResult, diskResult] = await Promise.allSettled([
6461
- (0, toolRegistry_1.executeTool)('system_info', {}),
6462
- (0, toolRegistry_1.executeTool)('shell_exec', { command: 'Get-Process | Where-Object {$_.MainWindowTitle -ne ""} | Select-Object Name, MainWindowTitle, @{N="RAM_MB";E={[math]::Round($_.WorkingSet64/1MB,1)}} | ConvertTo-Json -Compress' }),
6463
- (0, toolRegistry_1.executeTool)('shell_exec', { command: 'Get-PSDrive C | Select-Object @{N="UsedGB";E={[math]::Round(($_.Used/1GB),1)}}, @{N="FreeGB";E={[math]::Round(($_.Free/1GB),1)}} | ConvertTo-Json -Compress' }),
6464
- ]);
6465
- const sysInfo = sysResult.status === 'fulfilled' ? sysResult.value.output ?? '' : '';
6466
- const windows = windowsResult.status === 'fulfilled' ? windowsResult.value.output ?? '' : '';
6467
- const disk = diskResult.status === 'fulfilled' ? diskResult.value.output ?? '' : '';
6468
- firstMessageContext = `\n\nSYSTEM CONTEXT (you silently gathered this — use it to show awareness of the user’s machine):\nHardware: ${sysInfo}\nOpen windows: ${windows}\nDisk: ${disk}\n\nOn this first message, respond by showing what you can see on their machine right now. Be specific and natural — not a feature list. Example: \"I can see you have VS Code open, 6GB RAM free, and your disk is 78% full. What are you working on?\"`;
6469
- }
6470
- catch { /* silent — never block the response */ }
6471
- }
6472
6527
  const cognitionHint = userCognitionProfile_1.userCognitionProfile.getSystemPromptAddition();
6473
6528
  // Sprint 21: proactive memory surfacing
6474
6529
  let memoryContext = '';
@@ -6509,41 +6564,26 @@ async function streamChat(message, history, userName, _provider, model, apiName,
6509
6564
  memoryIndex = `\n\nMEMORY INDEX (topics you've learned about this user — use as background, not to recite):\n${idx}`;
6510
6565
  }
6511
6566
  catch { }
6512
- // [Aiden] System prompt v8SOUL.md + USER.md + STANDING_ORDERS injected
6513
- // Resolve real Windows username and home directory to prevent LLM from using "Aiden" as username
6567
+ // [Aiden] System prompt v9per-turn protected context (Option B hash-aware)
6568
+ // SOUL.md injected in full on first turn or when content changes; reference
6569
+ // line only when hash matches previous turn. USER/GOALS/SO/LESSONS always full.
6514
6570
  const _sysUser = process.env.USERNAME || process.env.USER || require('os').userInfo().username || 'User';
6515
6571
  const _sysHome = require('os').homedir();
6516
6572
  const systemContext = `\nSYSTEM CONTEXT — use these exact paths for ANY file operations:\n- Windows username: ${_sysUser} (NOT "Aiden" — Aiden is the AI name, not the Windows user)\n- Home directory: ${_sysHome}\n- Desktop: ${require('path').join(_sysHome, 'Desktop')}\n- Documents: ${require('path').join(_sysHome, 'Documents')}\n- Downloads: ${require('path').join(_sysHome, 'Downloads')}\n`;
6517
- const soulPrefix = aidenPersonality_1.SOUL ? aidenPersonality_1.SOUL + '\n\n' : '';
6518
- const userMdPath = path.join(WORKSPACE_ROOT, 'workspace', 'USER.md');
6519
- let userProfile = '';
6520
- if (fs.existsSync(userMdPath)) {
6521
- const raw = fs.readFileSync(userMdPath, 'utf8').trim();
6522
- if (raw && raw !== '# User Profile\nName: User' && raw !== '# User Profile') {
6523
- userProfile = '\nUSER PROFILE (read this — it describes the person you are talking to):\n' + raw + '\n';
6524
- }
6525
- }
6526
- if (!userProfile) {
6527
- // Fallback: at minimum tell Aiden the user's name from config
6528
- const cfg = (0, index_1.loadConfig)();
6529
- const name = cfg.user?.name || process.env.USER_NAME || userName;
6530
- if (name && name !== 'there' && name !== 'User') {
6531
- userProfile = `\nUSER PROFILE:\nName: ${name}\n`;
6532
- }
6533
- }
6534
- const soPath = path.join(WORKSPACE_ROOT, 'workspace', 'STANDING_ORDERS.md');
6535
- const standingOrders = fs.existsSync(soPath)
6536
- ? '\n\nSTANDING ORDERS — follow always:\n' + fs.readFileSync(soPath, 'utf-8')
6537
- : '';
6538
- const chatPrompt = `${soulPrefix}You are Aiden — a personal AI OS built for ${userName}. You are sharp, direct, and slightly witty. You speak like a trusted co-founder. Today: ${new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}.
6539
- ${userProfile}${systemContext}
6573
+ const _prevHash = sessionId ? soulHashBySession.get(sessionId) : undefined;
6574
+ const _ctx = protectedContext_1.protectedContextManager.getProtectedContext();
6575
+ const protectedBlock = (0, contextHandoff_1.buildProtectedContextBlock)(_ctx, _prevHash, sessionId);
6576
+ if (sessionId)
6577
+ soulHashBySession.set(sessionId, _ctx.hash);
6578
+ const chatPrompt = `${protectedBlock ? protectedBlock + '\n\n' : ''}You are Aiden a personal AI OS built for ${userName}. You are sharp, direct, and slightly witty. You speak like a trusted co-founder. Today: ${new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}.
6579
+ ${systemContext}
6540
6580
  HARD RULES — never violate:
6541
6581
  - Never say "As an AI language model...", "I'm here to assist", "Certainly!", "Great question!", "Of course!"
6542
6582
  - Never say "key findings from our research", "as per your request I have written", "here is a comparison of", "verdict:", "recommendation:" in a generic reply
6543
6583
  - Never mention Pega, BlueWinston, Gaude Digital, or any third-party product by name
6544
6584
  - Never say you can't access the internet (you have web_search) or can't create files (you have file_write)
6545
6585
  - Never fabricate capabilities: no graphic design, video production, or music generation
6546
- - Never list 250+ skills — you have 48 real tools, 31 specialist agents, and a 6-layer memory system
6586
+ - Never list 250+ skills — you have 72 real tools, 31 specialist agents, and a 6-layer memory system
6547
6587
  - For errors: explain what failed and what to try next
6548
6588
  - If you don't know something: say "I don't know"
6549
6589
  - Direct and concise: 1–3 sentences for simple results; more only when output is rich
@@ -6557,7 +6597,7 @@ IDENTITY — you are NOT a static pre-trained model. You have active living syst
6557
6597
  - Growth Engine: tracks failures, learns from them, improves over time
6558
6598
  - XP & Leveling: gains experience, streaks, and levels up
6559
6599
  When asked about capabilities or learning, be accurate. NEVER say you are just a pre-trained model that cannot learn.
6560
- ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessionContext}${memoryIndex}${standingOrders}`;
6600
+ ${cognitionHint}${memoryContext}${greetingPreamble}${sessionContext}${memoryIndex}`;
6561
6601
  const msgs = [
6562
6602
  { role: 'system', content: chatPrompt },
6563
6603
  ...history.slice(-8),
@@ -6664,8 +6704,9 @@ ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessi
6664
6704
  }
6665
6705
  else if (providerType === 'custom') {
6666
6706
  // ── Custom OpenAI-compatible endpoint — use the entry's own baseUrl ──
6707
+ const apiEntry = cfg.providers?.apis?.find((a) => a.name === responderChat.apiName);
6667
6708
  const customCp = cfg.customProviders?.find(c => c.id === responderChat.apiName);
6668
- const endpoint = customCp?.baseUrl || '';
6709
+ const endpoint = apiEntry?.baseUrl || customCp?.baseUrl || '';
6669
6710
  if (!endpoint)
6670
6711
  throw new Error(`Custom provider "${responderChat.apiName}" has no baseUrl`);
6671
6712
  const resp = await fetch(endpoint, {
@@ -6717,6 +6758,7 @@ ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessi
6717
6758
  cerebras: 'https://api.cerebras.ai/v1/chat/completions',
6718
6759
  openai: 'https://api.openai.com/v1/chat/completions',
6719
6760
  boa: 'https://api.bayofassets.com/v1/chat/completions',
6761
+ mistral: 'https://api.mistral.ai/v1/chat/completions',
6720
6762
  };
6721
6763
  const endpoint = ENDPOINTS[providerType] ?? ENDPOINTS['groq'];
6722
6764
  const resp = await fetch(endpoint, {
@@ -6776,6 +6818,7 @@ ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessi
6776
6818
  cerebras: 'https://api.cerebras.ai/v1/chat/completions',
6777
6819
  openai: 'https://api.openai.com/v1/chat/completions',
6778
6820
  boa: 'https://api.bayofassets.com/v1/chat/completions',
6821
+ mistral: 'https://api.mistral.ai/v1/chat/completions',
6779
6822
  };
6780
6823
  const fbEndpoint = ENDPOINTS[cloudTier.providerName] ?? ENDPOINTS['groq'];
6781
6824
  const fbHeaders = {
@@ -6829,7 +6872,7 @@ ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessi
6829
6872
  if (providerType !== 'ollama') {
6830
6873
  console.warn(`[Router] ${providerType} failed — falling back to Ollama`);
6831
6874
  try {
6832
- const ollamaModel = cfg.model?.activeModel || 'gemma4:e4b';
6875
+ const ollamaModel = cfg.ollama?.model || 'gemma4:e4b';
6833
6876
  const ollamaMs = (0, modelDiscovery_1.getOllamaTimeout)(ollamaModel); // full timeout — model may need to load
6834
6877
  const resp = await fetch('http://localhost:11434/api/chat', {
6835
6878
  method: 'POST',
@@ -6871,7 +6914,7 @@ ${cognitionHint}${firstMessageContext}${memoryContext}${greetingPreamble}${sessi
6871
6914
  }
6872
6915
  // Both failed — send a graceful error token
6873
6916
  console.error('[Router] All providers failed. Last error:', err?.message ?? 'unknown');
6874
- send({ token: `I'm temporarily unavailable my AI providers are at capacity. Please try again in a few minutes, or add more API keys in Settings → API Keys.`, done: false, provider: 'error' });
6917
+ send({ token: (0, diagnosticError_1.buildDiagnostic)({ tool: 'respond', provider: 'all', retries: 2, error: 'All AI providers failed or are at capacity', suggestion: 'Try again in a few minutes, or add more API keys in Settings → API Keys.' }), done: false, provider: 'error' });
6875
6918
  }
6876
6919
  streamEnded = true;
6877
6920
  clearTimeout(timeout);