aiden-runtime 3.19.4 → 3.19.5

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.
@@ -1,225 +1,225 @@
1
- {
2
- "user": {
3
- "name": "shiva"
4
- },
5
- "model": {
6
- "active": "groq-1",
7
- "activeModel": "llama-3.3-70b-versatile"
8
- },
9
- "providers": {
10
- "ollama": {
11
- "enabled": true,
12
- "models": []
13
- },
14
- "apis": [
15
- {
16
- "model": "gemini-3-flash",
17
- "enabled": false,
18
- "rateLimited": false,
19
- "usageCount": 0,
20
- "name": "boa-1",
21
- "provider": "boa",
22
- "key": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa"
23
- },
24
- {
25
- "model": "llama-3.3-70b-versatile",
26
- "enabled": true,
27
- "rateLimited": true,
28
- "usageCount": 72,
29
- "name": "groq-1",
30
- "provider": "groq",
31
- "key": "env:GROQ_API_KEY",
32
- "rateLimitedAt": 1777579113083,
33
- "rateLimitWindow": 300000
34
- },
35
- {
36
- "model": "llama-3.3-70b-versatile",
37
- "enabled": true,
38
- "rateLimited": true,
39
- "usageCount": 105,
40
- "name": "groq-2",
41
- "provider": "groq",
42
- "key": "env:GROQ_API_KEY_2"
43
- },
44
- {
45
- "model": "llama-3.3-70b-versatile",
46
- "enabled": true,
47
- "rateLimited": true,
48
- "usageCount": 41,
49
- "name": "groq-3",
50
- "provider": "groq",
51
- "key": "env:GROQ_API_KEY_3"
52
- },
53
- {
54
- "model": "llama-3.3-70b-versatile",
55
- "enabled": true,
56
- "rateLimited": true,
57
- "usageCount": 12,
58
- "name": "groq-4",
59
- "provider": "groq",
60
- "key": "env:GROQ_API_KEY_4"
61
- },
62
- {
63
- "model": "llama-3.3-70b-versatile",
64
- "enabled": true,
65
- "rateLimited": true,
66
- "usageCount": 7,
67
- "name": "groq-5",
68
- "provider": "groq",
69
- "key": "env:GROQ_API_KEY_5"
70
- },
71
- {
72
- "model": "llama-3.3-70b-versatile",
73
- "enabled": true,
74
- "rateLimited": false,
75
- "usageCount": 0,
76
- "name": "groq-6",
77
- "provider": "groq",
78
- "key": "env:GROQ_API_KEY_6"
79
- },
80
- {
81
- "model": "gemini-2.5-flash",
82
- "enabled": true,
83
- "rateLimited": false,
84
- "usageCount": 17,
85
- "name": "gemini-1",
86
- "provider": "gemini",
87
- "key": "env:GEMINI_API_KEY"
88
- },
89
- {
90
- "model": "gemini-2.5-flash",
91
- "enabled": true,
92
- "rateLimited": false,
93
- "usageCount": 9,
94
- "name": "gemini-2",
95
- "provider": "gemini",
96
- "key": "env:GEMINI_API_KEY_2"
97
- },
98
- {
99
- "model": "gemini-2.5-flash",
100
- "enabled": true,
101
- "rateLimited": false,
102
- "usageCount": 9,
103
- "name": "gemini-3",
104
- "provider": "gemini",
105
- "key": "env:GEMINI_API_KEY_3"
106
- },
107
- {
108
- "model": "gemini-2.5-flash",
109
- "enabled": true,
110
- "rateLimited": false,
111
- "usageCount": 9,
112
- "name": "gemini-4",
113
- "provider": "gemini",
114
- "key": "env:GEMINI_API_KEY_4"
115
- },
116
- {
117
- "model": "nvidia/llama-3.3-nemotron-super-49b-v1",
118
- "enabled": true,
119
- "rateLimited": false,
120
- "usageCount": 0,
121
- "name": "nvidia-1",
122
- "provider": "nvidia",
123
- "key": "env:NVIDIA_API_KEY"
124
- },
125
- {
126
- "model": "meta/llama-3.3-70b-instruct",
127
- "enabled": true,
128
- "rateLimited": false,
129
- "usageCount": 0,
130
- "name": "nvidia-2",
131
- "provider": "nvidia",
132
- "key": "env:NVIDIA_API_KEY"
133
- },
134
- {
135
- "model": "meta-llama/llama-3.3-70b-instruct:free",
136
- "enabled": true,
137
- "rateLimited": false,
138
- "usageCount": 7,
139
- "name": "openrouter-1",
140
- "provider": "openrouter",
141
- "key": "env:OPENROUTER_API_KEY"
142
- },
143
- {
144
- "model": "meta-llama/llama-3.3-70b-instruct:free",
145
- "enabled": true,
146
- "rateLimited": false,
147
- "usageCount": 6,
148
- "name": "openrouter-2",
149
- "provider": "openrouter",
150
- "key": "env:OPENROUTER_API_KEY_2"
151
- },
152
- {
153
- "model": "meta-llama/llama-3.3-70b-instruct:free",
154
- "enabled": true,
155
- "rateLimited": false,
156
- "usageCount": 6,
157
- "name": "openrouter-3",
158
- "provider": "openrouter",
159
- "key": "env:OPENROUTER_API_KEY_3"
160
- },
161
- {
162
- "model": "meta-llama/llama-3.1-405b-instruct",
163
- "enabled": true,
164
- "rateLimited": false,
165
- "usageCount": 7,
166
- "name": "together-1",
167
- "provider": "custom",
168
- "baseUrl": "https://api.together.xyz/v1/chat/completions",
169
- "key": "env:TOGETHER_API_KEY"
170
- },
171
- {
172
- "model": "gpt-4o-mini",
173
- "enabled": false,
174
- "rateLimited": false,
175
- "usageCount": 0,
176
- "name": "boa-2",
177
- "provider": "boa",
178
- "key": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa"
179
- }
180
- ]
181
- },
182
- "onboardingComplete": true,
183
- "telegram": {
184
- "enabled": false,
185
- "botToken": "",
186
- "allowedChatIds": [],
187
- "pollingInterval": 1000
188
- },
189
- "routing": {
190
- "mode": "auto",
191
- "fallbackToOllama": true
192
- },
193
- "ollama": {
194
- "fallbackModels": [],
195
- "baseUrl": "http://localhost:11434",
196
- "model": "gemma4:e4b",
197
- "plannerModel": "gemma4:e4b",
198
- "coderModel": "qwen2.5-coder:7b",
199
- "fastModel": "llama3.2:latest"
200
- },
201
- "customProviders": [
202
- {
203
- "id": "bayofassets-haiku",
204
- "displayName": "BayOfAssets Haiku 4.5",
205
- "baseUrl": "https://api.bayofassets.com/v1/chat/completions",
206
- "apiKey": "boa-dd14fba0-cdee-4247-ba03-90de01674c06",
207
- "model": "claude-haiku-4-5",
208
- "enabled": false,
209
- "tier": 1
210
- },
211
- {
212
- "id": "bayofassets",
213
- "displayName": "Bay of Assets",
214
- "baseUrl": "https://api.bayofassets.com/v1",
215
- "apiKey": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa",
216
- "model": "gemini-3-flash",
217
- "enabled": false,
218
- "tier": 2
219
- }
220
- ],
221
- "cli": {
222
- "theme": "ember"
223
- },
224
- "primaryProvider": ""
1
+ {
2
+ "user": {
3
+ "name": "shiva"
4
+ },
5
+ "model": {
6
+ "active": "groq-1",
7
+ "activeModel": "llama-3.3-70b-versatile"
8
+ },
9
+ "providers": {
10
+ "ollama": {
11
+ "enabled": true,
12
+ "models": []
13
+ },
14
+ "apis": [
15
+ {
16
+ "model": "gemini-3-flash",
17
+ "enabled": false,
18
+ "rateLimited": false,
19
+ "usageCount": 0,
20
+ "name": "boa-1",
21
+ "provider": "boa",
22
+ "key": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa"
23
+ },
24
+ {
25
+ "model": "llama-3.3-70b-versatile",
26
+ "enabled": true,
27
+ "rateLimited": true,
28
+ "usageCount": 72,
29
+ "name": "groq-1",
30
+ "provider": "groq",
31
+ "key": "env:GROQ_API_KEY",
32
+ "rateLimitedAt": 1777579113083,
33
+ "rateLimitWindow": 300000
34
+ },
35
+ {
36
+ "model": "llama-3.3-70b-versatile",
37
+ "enabled": true,
38
+ "rateLimited": true,
39
+ "usageCount": 105,
40
+ "name": "groq-2",
41
+ "provider": "groq",
42
+ "key": "env:GROQ_API_KEY_2"
43
+ },
44
+ {
45
+ "model": "llama-3.3-70b-versatile",
46
+ "enabled": true,
47
+ "rateLimited": true,
48
+ "usageCount": 41,
49
+ "name": "groq-3",
50
+ "provider": "groq",
51
+ "key": "env:GROQ_API_KEY_3"
52
+ },
53
+ {
54
+ "model": "llama-3.3-70b-versatile",
55
+ "enabled": true,
56
+ "rateLimited": true,
57
+ "usageCount": 12,
58
+ "name": "groq-4",
59
+ "provider": "groq",
60
+ "key": "env:GROQ_API_KEY_4"
61
+ },
62
+ {
63
+ "model": "llama-3.3-70b-versatile",
64
+ "enabled": true,
65
+ "rateLimited": true,
66
+ "usageCount": 7,
67
+ "name": "groq-5",
68
+ "provider": "groq",
69
+ "key": "env:GROQ_API_KEY_5"
70
+ },
71
+ {
72
+ "model": "llama-3.3-70b-versatile",
73
+ "enabled": true,
74
+ "rateLimited": false,
75
+ "usageCount": 0,
76
+ "name": "groq-6",
77
+ "provider": "groq",
78
+ "key": "env:GROQ_API_KEY_6"
79
+ },
80
+ {
81
+ "model": "gemini-2.5-flash",
82
+ "enabled": true,
83
+ "rateLimited": false,
84
+ "usageCount": 17,
85
+ "name": "gemini-1",
86
+ "provider": "gemini",
87
+ "key": "env:GEMINI_API_KEY"
88
+ },
89
+ {
90
+ "model": "gemini-2.5-flash",
91
+ "enabled": true,
92
+ "rateLimited": false,
93
+ "usageCount": 9,
94
+ "name": "gemini-2",
95
+ "provider": "gemini",
96
+ "key": "env:GEMINI_API_KEY_2"
97
+ },
98
+ {
99
+ "model": "gemini-2.5-flash",
100
+ "enabled": true,
101
+ "rateLimited": false,
102
+ "usageCount": 9,
103
+ "name": "gemini-3",
104
+ "provider": "gemini",
105
+ "key": "env:GEMINI_API_KEY_3"
106
+ },
107
+ {
108
+ "model": "gemini-2.5-flash",
109
+ "enabled": true,
110
+ "rateLimited": false,
111
+ "usageCount": 9,
112
+ "name": "gemini-4",
113
+ "provider": "gemini",
114
+ "key": "env:GEMINI_API_KEY_4"
115
+ },
116
+ {
117
+ "model": "nvidia/llama-3.3-nemotron-super-49b-v1",
118
+ "enabled": true,
119
+ "rateLimited": false,
120
+ "usageCount": 0,
121
+ "name": "nvidia-1",
122
+ "provider": "nvidia",
123
+ "key": "env:NVIDIA_API_KEY"
124
+ },
125
+ {
126
+ "model": "meta/llama-3.3-70b-instruct",
127
+ "enabled": true,
128
+ "rateLimited": false,
129
+ "usageCount": 0,
130
+ "name": "nvidia-2",
131
+ "provider": "nvidia",
132
+ "key": "env:NVIDIA_API_KEY"
133
+ },
134
+ {
135
+ "model": "meta-llama/llama-3.3-70b-instruct:free",
136
+ "enabled": true,
137
+ "rateLimited": false,
138
+ "usageCount": 7,
139
+ "name": "openrouter-1",
140
+ "provider": "openrouter",
141
+ "key": "env:OPENROUTER_API_KEY"
142
+ },
143
+ {
144
+ "model": "meta-llama/llama-3.3-70b-instruct:free",
145
+ "enabled": true,
146
+ "rateLimited": false,
147
+ "usageCount": 6,
148
+ "name": "openrouter-2",
149
+ "provider": "openrouter",
150
+ "key": "env:OPENROUTER_API_KEY_2"
151
+ },
152
+ {
153
+ "model": "meta-llama/llama-3.3-70b-instruct:free",
154
+ "enabled": true,
155
+ "rateLimited": false,
156
+ "usageCount": 6,
157
+ "name": "openrouter-3",
158
+ "provider": "openrouter",
159
+ "key": "env:OPENROUTER_API_KEY_3"
160
+ },
161
+ {
162
+ "model": "meta-llama/llama-3.1-405b-instruct",
163
+ "enabled": false,
164
+ "rateLimited": false,
165
+ "usageCount": 7,
166
+ "name": "together-1",
167
+ "provider": "custom",
168
+ "baseUrl": "https://api.together.xyz/v1/chat/completions",
169
+ "key": "env:TOGETHER_API_KEY"
170
+ },
171
+ {
172
+ "model": "gpt-4o-mini",
173
+ "enabled": false,
174
+ "rateLimited": false,
175
+ "usageCount": 0,
176
+ "name": "boa-2",
177
+ "provider": "boa",
178
+ "key": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa"
179
+ }
180
+ ]
181
+ },
182
+ "onboardingComplete": true,
183
+ "telegram": {
184
+ "enabled": false,
185
+ "botToken": "",
186
+ "allowedChatIds": [],
187
+ "pollingInterval": 1000
188
+ },
189
+ "routing": {
190
+ "mode": "auto",
191
+ "fallbackToOllama": true
192
+ },
193
+ "ollama": {
194
+ "fallbackModels": [],
195
+ "baseUrl": "http://localhost:11434",
196
+ "model": "gemma4:e4b",
197
+ "plannerModel": "gemma4:e4b",
198
+ "coderModel": "qwen2.5-coder:7b",
199
+ "fastModel": "llama3.2:latest"
200
+ },
201
+ "customProviders": [
202
+ {
203
+ "id": "bayofassets-haiku",
204
+ "displayName": "BayOfAssets Haiku 4.5",
205
+ "baseUrl": "https://api.bayofassets.com/v1/chat/completions",
206
+ "apiKey": "boa-dd14fba0-cdee-4247-ba03-90de01674c06",
207
+ "model": "claude-haiku-4-5",
208
+ "enabled": false,
209
+ "tier": 1
210
+ },
211
+ {
212
+ "id": "bayofassets",
213
+ "displayName": "Bay of Assets",
214
+ "baseUrl": "https://api.bayofassets.com/v1",
215
+ "apiKey": "boa-5a949b90-094c-4f35-a6ae-3fab8acc42aa",
216
+ "model": "gemini-3-flash",
217
+ "enabled": false,
218
+ "tier": 2
219
+ }
220
+ ],
221
+ "cli": {
222
+ "theme": "ember"
223
+ },
224
+ "primaryProvider": ""
225
225
  }
@@ -168,7 +168,7 @@
168
168
  },
169
169
  {
170
170
  "model": "meta-llama/llama-3.1-405b-instruct",
171
- "enabled": true,
171
+ "enabled": false,
172
172
  "rateLimited": false,
173
173
  "usageCount": 78,
174
174
  "name": "together-1",
@@ -6914,7 +6914,13 @@ ${cognitionHint}${memoryContext}${greetingPreamble}${sessionContext}${memoryInde
6914
6914
  }
6915
6915
  // Both failed — send a graceful error token
6916
6916
  console.error('[Router] All providers failed. Last error:', err?.message ?? 'unknown');
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' });
6917
+ const poolDiag = (0, router_1.diagnoseProviderPool)();
6918
+ send({ token: (0, diagnosticError_1.buildDiagnostic)({ tool: 'respond', provider: 'all', retries: 2,
6919
+ error: poolDiag.state === 'unconfigured' ? 'No API keys configured' : 'All AI providers failed or are at capacity',
6920
+ suggestion: poolDiag.state === 'unconfigured'
6921
+ ? 'Add API keys in Settings > API Keys, or start Ollama for local inference.'
6922
+ : 'Try again in a few minutes, or add more API keys in Settings > API Keys.',
6923
+ }), done: false, provider: 'error' });
6918
6924
  }
6919
6925
  streamEnded = true;
6920
6926
  clearTimeout(timeout);
@@ -1366,7 +1366,7 @@ Output ONLY valid JSON, nothing else:`;
1366
1366
  // Don't return early — let FORCE_RESPOND_TEST hook and PlannerGuard process the fallback plan
1367
1367
  console.warn('[Planner] All LLM attempts failed — respond fallback (going through guard)');
1368
1368
  parsed = {
1369
- plan: [{ step: 1, tool: 'respond', input: { message: (0, diagnosticError_1.buildDiagnostic)({ tool: 'planner', error: 'All LLM attempts failed', retries: 3, suggestion: 'Provider chain may be rate-limited. Try again in 12 minutes or rephrase your request.' }) }, description: 'Fallback response' }],
1369
+ plan: [{ step: 1, tool: 'respond', input: { message: (0, diagnosticError_1.buildDiagnostic)({ tool: 'planner', error: 'All LLM attempts failed', retries: 3, suggestion: (0, router_1.diagnoseProviderPool)().state === 'unconfigured' ? 'No API keys configured. Add keys in Settings > API Keys, or start Ollama for local inference.' : 'Provider chain is rate-limited. Try again in 1-2 minutes or rephrase your request.' }) }, description: 'Fallback response' }],
1370
1370
  requires_execution: true,
1371
1371
  goal: message,
1372
1372
  };
@@ -1492,7 +1492,9 @@ Output ONLY valid JSON, nothing else:`;
1492
1492
  tool: 'planner',
1493
1493
  error: 'Could not generate tool plan for action intent',
1494
1494
  retries: 1,
1495
- suggestion: 'Provider chain may be rate-limited. Try again in 1–2 minutes or use a more specific instruction.',
1495
+ suggestion: (0, router_1.diagnoseProviderPool)().state === 'unconfigured'
1496
+ ? 'No API keys configured. Add keys in Settings > API Keys, or start Ollama for local inference.'
1497
+ : 'Provider chain is rate-limited. Try again in 1-2 minutes or use a more specific instruction.',
1496
1498
  });
1497
1499
  }
1498
1500
  if (guardMatch) {
@@ -2737,7 +2739,10 @@ CRITICAL RULES FOR YOUR RESPONSE:
2737
2739
  if (successes.length > 0)
2738
2740
  parts.push(`Completed: ${successes.map(r => r.tool).join(', ')}.`);
2739
2741
  parts.push(`Failed: ${failures.map(r => `${r.tool} — ${r.error || 'unknown error'}`).join('; ')}.`);
2740
- parts.push('(All language providers are currently unavailable — full response cannot be generated.)');
2742
+ const poolDiag = (0, router_1.diagnoseProviderPool)();
2743
+ parts.push(`(${poolDiag.state === 'unconfigured'
2744
+ ? 'No API keys configured - add keys in Settings > API Keys'
2745
+ : 'All language providers are currently unavailable'} - full response cannot be generated.)`);
2741
2746
  onToken(parts.join(' '));
2742
2747
  return;
2743
2748
  }
@@ -41,6 +41,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
41
41
  };
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.SLASH_MIRROR_TOOL_NAMES = void 0;
44
+ exports.toolMemoryStore = toolMemoryStore;
45
+ exports.toolMemoryForget = toolMemoryForget;
44
46
  exports.registerSlashMirrorTools = registerSlashMirrorTools;
45
47
  // core/slashAsTool.ts — Phase 3: Slash commands mirrored as agent tools.
46
48
  //
@@ -2448,6 +2448,22 @@ public class AidenVolSet {
2448
2448
  return { success: false, output: '', error: e.message };
2449
2449
  }
2450
2450
  },
2451
+ // ── memory_store — persist a fact to permanent memory ──
2452
+ memory_store: async (p) => {
2453
+ const fact = p.fact || p.text || p.content || '';
2454
+ if (!fact)
2455
+ return { success: false, output: '', error: 'No fact provided' };
2456
+ const { toolMemoryStore } = await Promise.resolve().then(() => __importStar(require('./slashAsTool')));
2457
+ return toolMemoryStore(p);
2458
+ },
2459
+ // ── memory_forget — remove a fact from permanent memory ──
2460
+ memory_forget: async (p) => {
2461
+ const fact = p.fact || p.keyword || p.text || '';
2462
+ if (!fact)
2463
+ return { success: false, output: '', error: 'No fact provided' };
2464
+ const { toolMemoryForget } = await Promise.resolve().then(() => __importStar(require('./slashAsTool')));
2465
+ return toolMemoryForget(p);
2466
+ },
2451
2467
  // ── clarify — ask the user a multi-choice or free-text question mid-task ──
2452
2468
  clarify: async (p) => {
2453
2469
  const question = p.question || p.q || '';
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // AUTO-GENERATED by scripts/inject-version.js — do not edit by hand
5
- exports.VERSION = '3.19.4';
5
+ exports.VERSION = '3.19.5';
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.getLocalModels = getLocalModels;
8
8
  exports.initLocalModels = initLocalModels;
9
9
  exports.getOllamaModelForTask = getOllamaModelForTask;
10
+ exports.diagnoseProviderPool = diagnoseProviderPool;
10
11
  exports.getNextAvailableAPI = getNextAvailableAPI;
11
12
  exports.markRateLimited = markRateLimited;
12
13
  exports.markHealthy = markHealthy;
@@ -175,6 +176,36 @@ function autoResetExpiredLimits() {
175
176
  (0, index_1.saveConfig)(config);
176
177
  return changed;
177
178
  }
179
+ // ── Diagnose WHY the provider pool is empty ─────────────────
180
+ function diagnoseProviderPool() {
181
+ const config = (0, index_1.loadConfig)();
182
+ const allApis = mergeCustomProviders(config.providers.apis);
183
+ const enabled = allApis.filter(a => a.enabled);
184
+ const disabledCount = allApis.length - enabled.length;
185
+ let noKey = 0, rateLimited = 0;
186
+ for (const a of enabled) {
187
+ if (a.rateLimited) {
188
+ rateLimited++;
189
+ continue;
190
+ }
191
+ if (a.provider === 'custom')
192
+ continue;
193
+ const k = a.key.startsWith('env:') ? (process.env[a.key.replace('env:', '')] || '') : a.key;
194
+ if (k.length === 0)
195
+ noKey++;
196
+ }
197
+ const active = enabled.length - noKey - rateLimited;
198
+ if (active > 0)
199
+ return { state: 'ok', noKeyCount: noKey, rateLimitedCount: rateLimited, disabledCount: disabledCount, enabledCount: enabled.length, message: '' };
200
+ if (noKey > 0 && rateLimited === 0)
201
+ return { state: 'unconfigured', noKeyCount: noKey, rateLimitedCount: 0, disabledCount: disabledCount, enabledCount: enabled.length,
202
+ message: 'No API keys configured - add keys in Settings > API Keys or set env vars' };
203
+ if (rateLimited > 0 && noKey === 0)
204
+ return { state: 'rate-limited', noKeyCount: 0, rateLimitedCount: rateLimited, disabledCount: disabledCount, enabledCount: enabled.length,
205
+ message: `All ${rateLimited} cloud provider(s) rate-limited - retrying automatically` };
206
+ return { state: 'mixed', noKeyCount: noKey, rateLimitedCount: rateLimited, disabledCount: disabledCount, enabledCount: enabled.length,
207
+ message: `${noKey} provider(s) have no key, ${rateLimited} rate-limited` };
208
+ }
178
209
  // ── Get next available API — scored by response time + failures ──
179
210
  function getNextAvailableAPI() {
180
211
  autoResetExpiredLimits();
@@ -192,8 +223,12 @@ function getNextAvailableAPI() {
192
223
  : api.key;
193
224
  return resolvedKey.length > 0;
194
225
  });
195
- if (!available.length)
226
+ if (!available.length) {
227
+ const diag = diagnoseProviderPool();
228
+ if (diag.message)
229
+ console.log(`[Router] ${diag.message}`);
196
230
  return null;
231
+ }
197
232
  // Score: lower is better — blend usage count, response time, and failure history
198
233
  const primary = config.primaryProvider;
199
234
  const scored = available
@@ -390,7 +425,8 @@ function getModelForTask(task, message) {
390
425
  return resolveKey(chosen);
391
426
  }
392
427
  const model = getOllamaModelForTask(task === 'planner' ? 'planner' : 'responder');
393
- console.log(`[Router] ${task}: all cloud providers rate-limited - using Ollama ${model}`);
428
+ const diag = diagnoseProviderPool();
429
+ console.log(`[Router] ${task}: ${diag.message || 'all cloud providers unavailable'} - using Ollama ${model}`);
394
430
  return { apiKey: '', model, providerName: 'ollama', apiName: 'ollama' };
395
431
  }
396
432
  // Executor: fastest — cerebras > groq > nvidia → discovered fast model
@@ -401,7 +437,8 @@ function getModelForTask(task, message) {
401
437
  return resolveKey(api);
402
438
  }
403
439
  const model = getOllamaModelForTask('executor');
404
- console.log(`[Router] Executor: all cloud providers unavailable - falling back to Ollama ${model}`);
440
+ const diag = diagnoseProviderPool();
441
+ console.log(`[Router] Executor: ${diag.message || 'all cloud providers unavailable'} - falling back to Ollama ${model}`);
405
442
  return { apiKey: '', model, providerName: 'ollama', apiName: 'ollama' };
406
443
  }
407
444
  // Generic fallback — any available API, then gemma4:e4b
@@ -432,7 +469,8 @@ function getSmartProvider() {
432
469
  // FALLBACK: best discovered Ollama model
433
470
  if (config.routing?.fallbackToOllama !== false) {
434
471
  const model = getOllamaModelForTask('responder');
435
- console.log(`[Router] All APIs unavailable — falling back to Ollama ${model}`);
472
+ const diag = diagnoseProviderPool();
473
+ console.log(`[Router] ${diag.message || 'All APIs unavailable'} - falling back to Ollama ${model}`);
436
474
  return { provider: ollama_1.ollamaProvider, model, userName, apiName: 'ollama' };
437
475
  }
438
476
  // Last resort
@@ -477,8 +515,10 @@ function enterDegradedMode(reason) {
477
515
  }
478
516
  return {
479
517
  mode: 'degraded',
480
- message: `I'm temporarily running in limited mode — my AI providers ` +
481
- `are at capacity. I can still:\n` +
518
+ message: `I'm temporarily running in limited mode — ` +
519
+ `${diagnoseProviderPool().state === 'unconfigured'
520
+ ? 'no API keys are configured'
521
+ : 'my AI providers are at capacity'}. I can still:\n` +
482
522
  `• Search your files and memory\n` +
483
523
  `• Run scheduled tasks\n` +
484
524
  `• Execute shell commands and scripts\n` +