@blockrun/franklin 3.6.9 → 3.6.11
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/dist/agent/commands.js +1 -1
- package/dist/agent/llm.js +4 -3
- package/dist/agent/loop.js +2 -2
- package/dist/agent/types.d.ts +4 -2
- package/dist/commands/start.js +24 -5
- package/dist/ui/app.d.ts +1 -1
- package/dist/ui/app.js +3 -3
- package/package.json +1 -1
package/dist/agent/commands.js
CHANGED
|
@@ -627,7 +627,7 @@ export async function handleSlashCommand(input, ctx) {
|
|
|
627
627
|
const newModel = resolveModel(input.slice(7).trim());
|
|
628
628
|
ctx.config.model = newModel;
|
|
629
629
|
ctx.config.baseModel = newModel; // Update recovery target so loop doesn't reset
|
|
630
|
-
ctx.config.onModelChange?.(newModel);
|
|
630
|
+
ctx.config.onModelChange?.(newModel, 'user');
|
|
631
631
|
ctx.onEvent({ kind: 'text_delta', text: `Model → **${newModel}**\n` });
|
|
632
632
|
}
|
|
633
633
|
emitDone(ctx);
|
package/dist/agent/llm.js
CHANGED
|
@@ -105,14 +105,15 @@ export class ModelClient {
|
|
|
105
105
|
catch {
|
|
106
106
|
// Router not available (e.g. old build) — use hardcoded fallback table
|
|
107
107
|
}
|
|
108
|
-
// Static fallback if router is unavailable
|
|
108
|
+
// Static fallback if router is unavailable. Default to FREE model so
|
|
109
|
+
// users aren't silently charged when their intended model can't resolve.
|
|
109
110
|
const FALLBACKS = {
|
|
110
|
-
'blockrun/auto': '
|
|
111
|
+
'blockrun/auto': 'nvidia/nemotron-ultra-253b',
|
|
111
112
|
'blockrun/eco': 'nvidia/nemotron-ultra-253b',
|
|
112
113
|
'blockrun/premium': 'anthropic/claude-sonnet-4.6',
|
|
113
114
|
'blockrun/free': 'nvidia/nemotron-ultra-253b',
|
|
114
115
|
};
|
|
115
|
-
return FALLBACKS[model] || '
|
|
116
|
+
return FALLBACKS[model] || 'nvidia/nemotron-ultra-253b';
|
|
116
117
|
}
|
|
117
118
|
async *streamCompletion(request, signal) {
|
|
118
119
|
// Resolve virtual models before any API call
|
package/dist/agent/loop.js
CHANGED
|
@@ -298,7 +298,7 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
298
298
|
const baseModel = config.baseModel ?? config.model;
|
|
299
299
|
if (config.model !== baseModel && !paymentFailedModels.has(baseModel)) {
|
|
300
300
|
config.model = baseModel;
|
|
301
|
-
config.onModelChange?.(baseModel);
|
|
301
|
+
config.onModelChange?.(baseModel, 'system');
|
|
302
302
|
}
|
|
303
303
|
turnFailedModels = new Set(); // Fresh slate for transient failures this turn
|
|
304
304
|
const abort = new AbortController();
|
|
@@ -589,7 +589,7 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
589
589
|
if (nextFree) {
|
|
590
590
|
const oldModel = config.model;
|
|
591
591
|
config.model = nextFree;
|
|
592
|
-
config.onModelChange?.(nextFree);
|
|
592
|
+
config.onModelChange?.(nextFree, 'system');
|
|
593
593
|
onEvent({ kind: 'text_delta', text: `\n*${oldModel} failed — switching to ${nextFree}*\n` });
|
|
594
594
|
continue; // Retry with next model
|
|
595
595
|
}
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -140,8 +140,10 @@ export interface AgentConfig {
|
|
|
140
140
|
permissionPromptFn?: (toolName: string, description: string) => Promise<'yes' | 'no' | 'always'>;
|
|
141
141
|
/** Routes AskUser questions through ink UI input to avoid raw-mode stdin conflict */
|
|
142
142
|
onAskUser?: (question: string, options?: string[]) => Promise<string>;
|
|
143
|
-
/** Notify UI when agent switches model
|
|
144
|
-
|
|
143
|
+
/** Notify UI when agent switches model. `reason` is 'user' for explicit /model
|
|
144
|
+
* commands and 'system' for payment fallbacks or recovery. User-initiated
|
|
145
|
+
* changes must also update `baseModel`. */
|
|
146
|
+
onModelChange?: (model: string, reason?: 'user' | 'system') => void;
|
|
145
147
|
/** The user's intended model — updated by /model command, used for turn recovery */
|
|
146
148
|
baseModel?: string;
|
|
147
149
|
}
|
package/dist/commands/start.js
CHANGED
|
@@ -17,7 +17,8 @@ export async function startCommand(options) {
|
|
|
17
17
|
const chain = loadChain();
|
|
18
18
|
const apiUrl = API_URLS[chain];
|
|
19
19
|
const config = loadConfig();
|
|
20
|
-
// Resolve model — default to
|
|
20
|
+
// Resolve model — default to FREE (nemotron) so users don't get surprise charges.
|
|
21
|
+
// Paid models (glm-5.1, sonnet, opus, etc.) must be opted in with --model or /model.
|
|
21
22
|
let model;
|
|
22
23
|
const configModel = config['default-model'];
|
|
23
24
|
if (options.model) {
|
|
@@ -27,9 +28,19 @@ export async function startCommand(options) {
|
|
|
27
28
|
model = configModel;
|
|
28
29
|
}
|
|
29
30
|
else {
|
|
30
|
-
// Default:
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
// Default: free NVIDIA model — zero wallet charges until user explicitly switches
|
|
32
|
+
model = 'nvidia/nemotron-ultra-253b';
|
|
33
|
+
}
|
|
34
|
+
// Warn when a paid model is active so users know they'll be charged
|
|
35
|
+
const FREE_MODELS = new Set([
|
|
36
|
+
'nvidia/nemotron-ultra-253b',
|
|
37
|
+
'nvidia/qwen3-coder-480b',
|
|
38
|
+
'nvidia/devstral-2-123b',
|
|
39
|
+
'blockrun/free',
|
|
40
|
+
]);
|
|
41
|
+
if (!FREE_MODELS.has(model)) {
|
|
42
|
+
console.log(chalk.yellow(` Model: ${model} (paid — charges from your wallet per call)`));
|
|
43
|
+
console.log(chalk.dim(` Switch to free with: /model free\n`));
|
|
33
44
|
}
|
|
34
45
|
// Auto-create wallet if needed (no interruption — free models work without funding)
|
|
35
46
|
let walletAddress = '';
|
|
@@ -184,8 +195,13 @@ async function runWithInkUI(agentConfig, model, workDir, version, walletInfo, on
|
|
|
184
195
|
walletAddress: walletInfo?.address,
|
|
185
196
|
walletBalance: walletInfo?.balance,
|
|
186
197
|
chain: walletInfo?.chain,
|
|
187
|
-
onModelChange: (newModel) => {
|
|
198
|
+
onModelChange: (newModel, reason) => {
|
|
188
199
|
agentConfig.model = newModel;
|
|
200
|
+
// User-initiated switch must also update baseModel so the agent loop
|
|
201
|
+
// doesn't revert to the previous model on the next turn.
|
|
202
|
+
if (reason === 'user') {
|
|
203
|
+
agentConfig.baseModel = newModel;
|
|
204
|
+
}
|
|
189
205
|
},
|
|
190
206
|
});
|
|
191
207
|
// Wire permission prompts through Ink UI to avoid stdin/readline conflict.
|
|
@@ -329,12 +345,14 @@ async function handleSlashCommand(cmd, config, ui) {
|
|
|
329
345
|
const newModel = parts[1];
|
|
330
346
|
if (newModel) {
|
|
331
347
|
config.model = resolveModel(newModel);
|
|
348
|
+
config.baseModel = config.model;
|
|
332
349
|
console.error(chalk.green(` Model → ${config.model}`));
|
|
333
350
|
return null;
|
|
334
351
|
}
|
|
335
352
|
const picked = await pickModel(config.model);
|
|
336
353
|
if (picked) {
|
|
337
354
|
config.model = picked;
|
|
355
|
+
config.baseModel = picked;
|
|
338
356
|
console.error(chalk.green(` Model → ${config.model}`));
|
|
339
357
|
}
|
|
340
358
|
return null;
|
|
@@ -343,6 +361,7 @@ async function handleSlashCommand(cmd, config, ui) {
|
|
|
343
361
|
const picked = await pickModel(config.model);
|
|
344
362
|
if (picked) {
|
|
345
363
|
config.model = picked;
|
|
364
|
+
config.baseModel = picked;
|
|
346
365
|
console.error(chalk.green(` Model → ${config.model}`));
|
|
347
366
|
}
|
|
348
367
|
return null;
|
package/dist/ui/app.d.ts
CHANGED
package/dist/ui/app.js
CHANGED
|
@@ -219,7 +219,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
219
219
|
else if (key.return) {
|
|
220
220
|
const selected = PICKER_MODELS_FLAT[pickerIdx];
|
|
221
221
|
setCurrentModel(selected.id);
|
|
222
|
-
onModelChange(selected.id);
|
|
222
|
+
onModelChange(selected.id, 'user');
|
|
223
223
|
showStatus(`Model → ${selected.label}`, 'success', 3000);
|
|
224
224
|
setMode('input');
|
|
225
225
|
setReady(true);
|
|
@@ -290,7 +290,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
290
290
|
if (parts[1]) {
|
|
291
291
|
const resolved = resolveModel(parts[1]);
|
|
292
292
|
setCurrentModel(resolved);
|
|
293
|
-
onModelChange(resolved);
|
|
293
|
+
onModelChange(resolved, 'user');
|
|
294
294
|
showStatus(`Model → ${resolved}`, 'success', 3000);
|
|
295
295
|
}
|
|
296
296
|
else {
|
|
@@ -685,7 +685,7 @@ export function launchInkUI(opts) {
|
|
|
685
685
|
// Agent loop hasn't called waitForInput yet — queue the input
|
|
686
686
|
pendingInput = value;
|
|
687
687
|
}
|
|
688
|
-
}, onModelChange: (model) => { opts.onModelChange?.(model); }, onAbort: () => { abortCallback?.(); }, onExit: () => {
|
|
688
|
+
}, onModelChange: (model, reason) => { opts.onModelChange?.(model, reason); }, onAbort: () => { abortCallback?.(); }, onExit: () => {
|
|
689
689
|
exiting = true;
|
|
690
690
|
if (resolveInput) {
|
|
691
691
|
resolveInput(null);
|
package/package.json
CHANGED