opencode-pollinations-plugin 5.4.8-debug.1 → 5.5.0-debug.3

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.
@@ -174,32 +174,29 @@ async function handleUsageCommand(args) {
174
174
  }
175
175
  }
176
176
  function handleFallbackCommand(args) {
177
- const [main, agent] = args;
178
- if (!main) {
177
+ const [primary, subagent] = args;
178
+ if (!primary) {
179
179
  const config = loadConfig();
180
- const freeConfig = `Free: main=${config.fallbacks.free.main}, agent=${config.fallbacks.free.agent}`;
181
- const enterConfig = `Enter: agent=${config.fallbacks.enter.agent}`;
180
+ const freeConfig = `Free Fallbacks: primary=${config.fallbacks.free.primary}, subagent=${config.fallbacks.free.subagent}`;
181
+ const enterConfig = `Enter Targets: subagent=${config.fallbacks.enter.subagent}`;
182
182
  return {
183
183
  handled: true,
184
- response: `Fallbacks actuels:\n${freeConfig}\n${enterConfig}`
184
+ response: `Configuration des Modèles:\n${freeConfig}\n${enterConfig}`
185
185
  };
186
186
  }
187
- // Default behavior for "/poll fallback <model> <agent>" is setting FREE fallbacks
188
- // User needs to use commands (maybe add /poll fallback enter ...) later
189
- // For now, map to Free Fallback as it's the primary Safety Net
190
187
  const config = loadConfig();
191
188
  saveConfig({
192
189
  fallbacks: {
193
190
  ...config.fallbacks,
194
191
  free: {
195
- main: main,
196
- agent: agent || config.fallbacks.free.agent
192
+ primary: primary,
193
+ subagent: subagent || config.fallbacks.free.subagent
197
194
  }
198
195
  }
199
196
  });
200
197
  return {
201
198
  handled: true,
202
- response: `✅ Fallback (Free) configuré: main=${main}, agent=${agent || config.fallbacks.free.agent}`
199
+ response: `✅ Fallbacks configurés: primary=${primary}, subagent=${subagent || config.fallbacks.free.subagent}`
203
200
  };
204
201
  }
205
202
  function handleConfigCommand(args) {
@@ -273,7 +270,7 @@ function handleHelpCommand() {
273
270
 
274
271
  - **\`/pollinations mode [mode]\`**: Change le mode (manual, alwaysfree, pro).
275
272
  - **\`/pollinations usage [full]\`**: Affiche le dashboard (full = détail).
276
- - **\`/pollinations fallback <main> [agent]\`**: Configure le Safety Net (Free).
273
+ - **\`/pollinations fallback <primary> [subagent]\`**: Configure le Safety Net (Free).
277
274
  - **\`/pollinations config [key] [value]\`**:
278
275
  - \`status_gui\`: none, alert, all (Status Dashboard).
279
276
  - \`logs_gui\`: none, error, verbose (Logs Techniques).
@@ -12,11 +12,11 @@ export interface PollinationsConfigV5 {
12
12
  };
13
13
  fallbacks: {
14
14
  free: {
15
- main: string;
16
- agent: string;
15
+ primary: string;
16
+ subagent: string;
17
17
  };
18
18
  enter: {
19
- agent: string;
19
+ subagent: string;
20
20
  };
21
21
  };
22
22
  enablePaidTools: boolean;
@@ -37,11 +37,11 @@ export declare function saveConfig(updates: Partial<PollinationsConfigV5>): {
37
37
  };
38
38
  fallbacks: {
39
39
  free: {
40
- main: string;
41
- agent: string;
40
+ primary: string;
41
+ subagent: string;
42
42
  };
43
43
  enter: {
44
- agent: string;
44
+ subagent: string;
45
45
  };
46
46
  };
47
47
  enablePaidTools: boolean;
@@ -9,13 +9,11 @@ const CONFIG_DIR_OPENCODE = path.join(HOMEDIR, '.config', 'opencode');
9
9
  const OPENCODE_CONFIG_FILE = path.join(CONFIG_DIR_OPENCODE, 'opencode.json');
10
10
  const AUTH_FILE = path.join(HOMEDIR, '.local', 'share', 'opencode', 'auth.json');
11
11
  // LOAD PACKAGE VERSION
12
- let PKG_VERSION = '5.2.0';
12
+ let PKG_VERSION = '5.5.0';
13
13
  try {
14
- const pkgPath = path.join(__dirname, '../../package.json');
15
- if (fs.existsSync(pkgPath)) {
16
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
17
- PKG_VERSION = pkg.version;
18
- }
14
+ const pkgPath = new URL('../../package.json', import.meta.url);
15
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
16
+ PKG_VERSION = pkg.version;
19
17
  }
20
18
  catch (e) { }
21
19
  const DEFAULT_CONFIG_V5 = {
@@ -24,8 +22,8 @@ const DEFAULT_CONFIG_V5 = {
24
22
  gui: { status: 'alert', logs: 'none' },
25
23
  thresholds: { tier: 10, wallet: 5 },
26
24
  fallbacks: {
27
- free: { main: 'free/mistral', agent: 'free/openai-fast' },
28
- enter: { agent: 'free/gemini' }
25
+ free: { primary: 'free/mistral', subagent: 'free/openai-fast' },
26
+ enter: { subagent: 'default' }
29
27
  },
30
28
  enablePaidTools: false,
31
29
  statusBar: true
@@ -40,7 +38,6 @@ function logConfig(msg) {
40
38
  catch (e) { }
41
39
  }
42
40
  // SIMPLE LOAD (Direct Disk Read - No Caching, No Watchers)
43
- // This ensures the Proxy ALWAYS sees the latest state from auth.json
44
41
  export function loadConfig() {
45
42
  return readConfigFromDisk();
46
43
  }
@@ -52,6 +49,23 @@ function readConfigFromDisk() {
52
49
  if (fs.existsSync(CONFIG_FILE)) {
53
50
  const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
54
51
  const custom = JSON.parse(raw);
52
+ // MIGRATION LOGIC (v5.5.0)
53
+ if (custom.fallbacks) {
54
+ if (custom.fallbacks.free) {
55
+ if (custom.fallbacks.free.main) {
56
+ custom.fallbacks.free.primary = custom.fallbacks.free.main;
57
+ delete custom.fallbacks.free.main;
58
+ }
59
+ if (custom.fallbacks.free.agent) {
60
+ custom.fallbacks.free.subagent = custom.fallbacks.free.agent;
61
+ delete custom.fallbacks.free.agent;
62
+ }
63
+ }
64
+ if (custom.fallbacks.enter && custom.fallbacks.enter.agent) {
65
+ custom.fallbacks.enter.subagent = custom.fallbacks.enter.agent;
66
+ delete custom.fallbacks.enter.agent;
67
+ }
68
+ }
55
69
  config = { ...config, ...custom };
56
70
  if (config.apiKey)
57
71
  keyFound = true;
@@ -33,37 +33,6 @@ function saveSignatureMap() {
33
33
  log(`ERROR: Error mapping signature: ${String(e)}`);
34
34
  }
35
35
  }
36
- /**
37
- * DEEP INSPECTION LOGGING (Temporary for v5.5.0 Analysis)
38
- * Dumps the full payload to a local log file to identify OpenCode agent signatures.
39
- */
40
- function dumpPayload(body) {
41
- try {
42
- const logPath = '/home/fkomp/Bureau/oracle/opencode-pollinations-plugin/logs/payload_dump.log';
43
- if (!fs.existsSync(path.dirname(logPath)))
44
- fs.mkdirSync(path.dirname(logPath), { recursive: true });
45
- // Extract interesting bits for quick reading
46
- const systemPrompt = body.messages?.find((m) => m.role === 'system')?.content || "NONE";
47
- const inspection = {
48
- timestamp: new Date().toISOString(),
49
- captured_fields: {
50
- model: body.model,
51
- user: body.user,
52
- metadata: body.metadata,
53
- agent: body.agent,
54
- hasTools: !!(body.tools && body.tools.length > 0),
55
- toolsCount: body.tools?.length || 0,
56
- messageCount: body.messages?.length || 0,
57
- systemPromptSnippet: systemPrompt.substring(0, 300) + "..."
58
- },
59
- full_body: body
60
- };
61
- fs.appendFileSync(logPath, JSON.stringify(inspection, null, 2) + "\n\n--- END OF REQUEST ---\n\n");
62
- }
63
- catch (e) {
64
- console.error("[DebugLog] Failed to dump payload:", e);
65
- }
66
- }
67
36
  // RECURSIVE NORMALIZER for Stable Hashing
68
37
  function normalizeContent(c) {
69
38
  if (!c)
@@ -181,10 +150,15 @@ export async function handleChatCompletion(req, res, bodyRaw) {
181
150
  let authHeader = undefined;
182
151
  try {
183
152
  const body = JSON.parse(bodyRaw);
184
- dumpPayload(body); // <--- DEEP INSPECTION
185
153
  const config = loadConfig();
186
- // DEBUG: Trace Config State for Hot Reload verification
187
- log(`[Proxy Request] Config Loaded. Mode: ${config.mode}, HasKey: ${!!config.apiKey}, KeyLength: ${config.apiKey ? config.apiKey.length : 0}`);
154
+ // --- AGENT DETECTION (v5.5.0 Simplified) ---
155
+ const systemMsg = body.messages?.find((m) => m.role === 'system')?.content || "";
156
+ const isSubagent = systemMsg.includes("exploring codebases") ||
157
+ systemMsg.includes("title generator") ||
158
+ systemMsg.includes("researching complex questions") ||
159
+ systemMsg.includes("session summary");
160
+ const agentLabel = isSubagent ? "Subagent" : "Primary";
161
+ log(`[Proxy] Agent Detected: ${agentLabel} (Tools: ${body.tools?.length || 0})`);
188
162
  // 0. COMMAND HANDLING
189
163
  if (body.messages && body.messages.length > 0) {
190
164
  const lastMsg = body.messages[body.messages.length - 1];
@@ -253,50 +227,26 @@ export async function handleChatCompletion(req, res, bodyRaw) {
253
227
  actualModel = actualModel.replace('free/', '');
254
228
  }
255
229
  // B. SAFETY NETS (The Core V5 Logic)
256
- if (config.mode === 'alwaysfree') {
257
- if (isEnterprise) {
258
- if (quota.tier === 'error') {
259
- log(`[SafetyNet] AlwaysFree Mode: Quota Check Failed. Switching to Free Fallback.`);
260
- actualModel = config.fallbacks.free.main.replace('free/', '');
230
+ // B. SPECIALIZED TARGETS (Subagents in Enter Universe)
231
+ if (isEnterprise && isSubagent) {
232
+ const specialized = config.fallbacks.enter.subagent;
233
+ if (specialized && specialized !== 'default') {
234
+ actualModel = specialized.replace('enter/', '').replace('free/', '');
235
+ if (specialized.startsWith('free/'))
261
236
  isEnterprise = false;
262
- isFallbackActive = true;
263
- fallbackReason = "Quota Unreachable (Safety)";
264
- }
265
- else {
266
- const tierRatio = quota.tierLimit > 0 ? (quota.tierRemaining / quota.tierLimit) : 0;
267
- if (tierRatio <= (config.thresholds.tier / 100)) {
268
- log(`[SafetyNet] AlwaysFree Mode: Tier (${(tierRatio * 100).toFixed(1)}%) <= Threshold (${config.thresholds.tier}%). Switching.`);
269
- actualModel = config.fallbacks.free.main.replace('free/', '');
270
- isEnterprise = false;
271
- isFallbackActive = true;
272
- fallbackReason = `Daily Tier < ${config.thresholds.tier}% (Wallet Protected)`;
273
- }
274
- }
237
+ log(`[Route] Subagent specialized to: ${actualModel}`);
275
238
  }
276
239
  }
277
- else if (config.mode === 'pro') {
240
+ // C. SAFETY NETS (The Core V5 Logic)
241
+ if (config.mode === 'alwaysfree' || config.mode === 'pro') {
278
242
  if (isEnterprise) {
279
- if (quota.tier === 'error') {
280
- log(`[SafetyNet] Pro Mode: Quota Unreachable. Switching to Free Fallback.`);
281
- actualModel = config.fallbacks.free.main.replace('free/', '');
243
+ if (!quota.canUseEnterprise || quota.tier === 'error') {
244
+ const fallbackModel = isSubagent ? config.fallbacks.free.subagent : config.fallbacks.free.primary;
245
+ log(`[SafetyNet] ${config.mode.toUpperCase()} Limit reached. Switching to: ${fallbackModel}`);
246
+ actualModel = fallbackModel.replace('free/', '');
282
247
  isEnterprise = false;
283
248
  isFallbackActive = true;
284
- fallbackReason = "Quota Unreachable (Safety)";
285
- }
286
- else {
287
- const tierRatio = quota.tierLimit > 0 ? (quota.tierRemaining / quota.tierLimit) : 0;
288
- // Logic: Fallback if Wallet is Low (< Threshold) AND Tier is Exhausted (< Threshold %)
289
- // Wait, user wants priority to Free Tier.
290
- // If Free Tier is available (Ratio > Threshold), we usage it (don't fallback).
291
- // If Free Tier is exhausted (Ratio <= Threshold), THEN check Wallet.
292
- // If Wallet also Low, THEN Fallback.
293
- if (quota.walletBalance < config.thresholds.wallet && tierRatio <= (config.thresholds.tier / 100)) {
294
- log(`[SafetyNet] Pro Mode: Wallet < $${config.thresholds.wallet} AND Tier < ${config.thresholds.tier}%. Switching.`);
295
- actualModel = config.fallbacks.free.main.replace('free/', '');
296
- isEnterprise = false;
297
- isFallbackActive = true;
298
- fallbackReason = `Wallet & Tier Critical`;
299
- }
249
+ fallbackReason = `${config.mode.toUpperCase()} Limit Reached`;
300
250
  }
301
251
  }
302
252
  }
@@ -502,7 +452,7 @@ export async function handleChatCompletion(req, res, bodyRaw) {
502
452
  log(`[SafetyNet] Upstream Rejection (${fetchRes.status}). Triggering Transparent Fallback.`);
503
453
  if (isEnterpriseFallback) {
504
454
  // 1a. Enterprise -> Free Fallback
505
- actualModel = config.fallbacks.free.main.replace('free/', '');
455
+ actualModel = config.fallbacks.free.primary.replace('free/', '');
506
456
  isEnterprise = false;
507
457
  isFallbackActive = true;
508
458
  if (fetchRes.status === 402)
@@ -66,6 +66,12 @@ export async function getQuotaStatus(forceRefresh = false) {
66
66
  // Le wallet c'est le reste (balance totale - ce qu'il reste du tier gratuit non consommé)
67
67
  const walletBalance = Math.max(0, balance - cleanTierRemaining);
68
68
  const cleanWalletBalance = Math.max(0, parseFloat(walletBalance.toFixed(4)));
69
+ // Calculer les marges de sécurité basées sur les seuils configurés
70
+ const tierThresholdPollen = (config.thresholds.tier / 100) * tierLimit;
71
+ const walletThresholdUSD = config.thresholds.wallet; // Wallet threshold is already in $ in config
72
+ // canUseEnterprise: Vrai si on a encore du budget au-dessus du seuil de fallback
73
+ const hasAvailableTier = cleanTierRemaining > tierThresholdPollen;
74
+ const hasAvailableWallet = cleanWalletBalance > walletThresholdUSD;
69
75
  cachedQuota = {
70
76
  tierRemaining: cleanTierRemaining,
71
77
  tierUsed,
@@ -73,9 +79,9 @@ export async function getQuotaStatus(forceRefresh = false) {
73
79
  walletBalance: cleanWalletBalance,
74
80
  nextResetAt: resetInfo.nextReset,
75
81
  timeUntilReset: resetInfo.timeUntilReset,
76
- canUseEnterprise: cleanTierRemaining > 0.05 || cleanWalletBalance > 0.05,
77
- isUsingWallet: cleanTierRemaining <= 0.05 && cleanWalletBalance > 0.05,
78
- needsAlert: tierLimit > 0 ? (cleanTierRemaining / tierLimit * 100) <= config.thresholds.tier : false,
82
+ canUseEnterprise: hasAvailableTier || hasAvailableWallet,
83
+ isUsingWallet: !hasAvailableTier && hasAvailableWallet,
84
+ needsAlert: tierLimit > 0 ? (cleanTierRemaining <= tierThresholdPollen) : false,
79
85
  tier: profile.tier,
80
86
  tierEmoji: tierInfo.emoji
81
87
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-pollinations-plugin",
3
3
  "displayName": "Pollinations AI (V5.1)",
4
- "version": "5.4.8-debug.1",
4
+ "version": "5.5.0-debug.3",
5
5
  "description": "Native Pollinations.ai Provider Plugin for OpenCode",
6
6
  "publisher": "pollinations",
7
7
  "repository": {