@blockrun/franklin 3.8.11 → 3.8.12
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 +13 -2
- package/dist/agent/context.js +5 -2
- package/dist/agent/loop.js +11 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +9 -6
- package/dist/index.js +1 -1
- package/dist/tools/tool-categories.d.ts +13 -8
- package/dist/tools/tool-categories.js +30 -9
- package/package.json +1 -1
package/dist/agent/commands.js
CHANGED
|
@@ -203,7 +203,7 @@ const DIRECT_COMMANDS = {
|
|
|
203
203
|
` **Analysis:** /security /lint /optimize /todo /deps /clean /migrate /doc\n` +
|
|
204
204
|
` **Session:** /plan /ultraplan /execute /compact /retry /sessions /resume /session-search /context /tasks\n` +
|
|
205
205
|
` **Power:** /ultrathink [query] /ultraplan /noplan /moa [query] /dump\n` +
|
|
206
|
-
` **Info:** /model /wallet /cost /tokens /learnings /brain /mcp /doctor /version /bug /help\n` +
|
|
206
|
+
` **Info:** /model /auto /wallet /cost /tokens /learnings /brain /mcp /doctor /version /bug /help\n` +
|
|
207
207
|
` **UI:** /clear /exit\n` +
|
|
208
208
|
(ultrathinkOn ? `\n Ultrathink: ON\n` : '')
|
|
209
209
|
});
|
|
@@ -616,6 +616,17 @@ export async function handleSlashCommand(input, ctx) {
|
|
|
616
616
|
emitDone(ctx);
|
|
617
617
|
return { handled: true };
|
|
618
618
|
}
|
|
619
|
+
// /auto — hard-reset to smart routing regardless of current state.
|
|
620
|
+
// Shortcut for `/model auto`. Fixes the common "I got stuck on a model
|
|
621
|
+
// and want Franklin to pick again" scenario without typing the long form.
|
|
622
|
+
if (input === '/auto') {
|
|
623
|
+
ctx.config.model = 'blockrun/auto';
|
|
624
|
+
ctx.config.baseModel = 'blockrun/auto';
|
|
625
|
+
ctx.config.onModelChange?.('blockrun/auto', 'user');
|
|
626
|
+
ctx.onEvent({ kind: 'text_delta', text: `Model → **Auto** (smart routing re-enabled)\n` });
|
|
627
|
+
emitDone(ctx);
|
|
628
|
+
return { handled: true };
|
|
629
|
+
}
|
|
619
630
|
// /model — show current model or switch with /model <name>
|
|
620
631
|
if (input === '/model' || input.startsWith('/model ')) {
|
|
621
632
|
if (input === '/model') {
|
|
@@ -920,7 +931,7 @@ export async function handleSlashCommand(input, ctx) {
|
|
|
920
931
|
...Object.keys(DIRECT_COMMANDS),
|
|
921
932
|
...Object.keys(REWRITE_COMMANDS),
|
|
922
933
|
...ARG_COMMANDS.map(c => c.prefix.trim()),
|
|
923
|
-
'/branch', '/resume', '/model', '/wallet', '/cost', '/help', '/clear', '/retry', '/exit', '/session-search', '/ssearch', '/failures',
|
|
934
|
+
'/branch', '/resume', '/model', '/auto', '/wallet', '/cost', '/help', '/clear', '/retry', '/exit', '/session-search', '/ssearch', '/failures',
|
|
924
935
|
];
|
|
925
936
|
const cmd = input.split(/\s/)[0];
|
|
926
937
|
const close = allCommands.filter(c => {
|
package/dist/agent/context.js
CHANGED
|
@@ -158,9 +158,12 @@ function getToolPatternsSection() {
|
|
|
158
158
|
- **Understanding code**: Glob for structure → Read key files → Grep for specific symbols/patterns. Don't read every file in a directory.
|
|
159
159
|
- **Making changes**: Read the file → Edit with targeted replacement → verify the edit worked (Read again or run tests). Never Edit without Reading first.
|
|
160
160
|
- **Running commands**: Use Bash for shell operations that have no dedicated tool. Chain commands with && when sequential. Use separate Bash calls when you need to inspect intermediate output.
|
|
161
|
-
- **
|
|
161
|
+
- **Financial / market questions**: If the user asks about ANY ticker, price, stock, crypto, FX, commodity, or "should I sell / hold X" — call **TradingMarket** FIRST to get the live quote before answering. Never answer from memory for price / market data. The agent has a wallet and the tool costs \$0 for crypto/FX/commodity and \$0.001 for stocks — use it. Examples that MUST call TradingMarket: "how is CRCL doing", "BTC 现在多少钱", "should I sell AAPL", "EUR-USD rate", "gold price today".
|
|
162
|
+
- **Current events / "what happened to X" / "why did X drop"**: call **ExaAnswer** for a cited synthesized answer. For breadth use **ExaSearch** then **ExaReadUrls** on the best results. Never guess at recent events from training data — the model cutoff is older than the question.
|
|
163
|
+
- **General web research**: WebSearch for discovery → WebFetch for specific URLs from search results. Don't WebFetch URLs you invented.
|
|
162
164
|
- **Complex tasks**: Use Agent to spawn sub-agents for 2+ independent research or implementation tasks. Don't do sequentially what can be done in parallel.
|
|
163
|
-
- **Multiple independent lookups**: Call all tools in a single response. NEVER make sequential calls when parallel calls would work
|
|
165
|
+
- **Multiple independent lookups**: Call all tools in a single response. NEVER make sequential calls when parallel calls would work.
|
|
166
|
+
- **Don't ask the user for what a tool can answer.** If they say "Circle stock" and you don't know the ticker, call ExaAnswer or TradingMarket rather than demanding they supply a ticker symbol.`;
|
|
164
167
|
}
|
|
165
168
|
function getTokenEfficiencySection() {
|
|
166
169
|
return `# Token Efficiency
|
package/dist/agent/loop.js
CHANGED
|
@@ -694,6 +694,16 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
694
694
|
routingSavings = routing.savings;
|
|
695
695
|
lastRoutedModel = routing.model;
|
|
696
696
|
lastRoutedCategory = routing.signals[0] || '';
|
|
697
|
+
// Surface the routing decision so users know which concrete model
|
|
698
|
+
// just got picked. Without this the status bar reads "auto" and
|
|
699
|
+
// users have no idea what's actually running — or worse, they
|
|
700
|
+
// believe they're stuck on the last-seen concrete name.
|
|
701
|
+
if (loopCount === 1) {
|
|
702
|
+
onEvent({
|
|
703
|
+
kind: 'text_delta',
|
|
704
|
+
text: `*Auto → ${routing.model}*\n\n`,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
697
707
|
}
|
|
698
708
|
// Update token estimation model for more accurate byte-per-token ratio
|
|
699
709
|
setEstimationModel(resolvedModel);
|
|
@@ -964,7 +974,7 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
964
974
|
+ (usage.outputTokens / 1_000_000) * OPUS_PRICING.output;
|
|
965
975
|
sessionSavedVsOpus += Math.max(0, opusCost - costEstimate);
|
|
966
976
|
// ── Max-spend guard ──
|
|
967
|
-
// Session-level cost ceiling.
|
|
977
|
+
// Session-level cost ceiling. Batch/scripted callers pass this to bound a
|
|
968
978
|
// single run ("spend at most $0.50 for today's digest"); interactive
|
|
969
979
|
// users can pass it to feel safe walking away. Hits as soon as accumulated
|
|
970
980
|
// cost crosses the cap — the last call that tipped us over still runs,
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -158,7 +158,7 @@ export interface AgentConfig {
|
|
|
158
158
|
/**
|
|
159
159
|
* Hard cap on total USD spend for this session. When accumulated API cost
|
|
160
160
|
* crosses the cap, the loop stops with `reason: 'budget'`. Zero/negative
|
|
161
|
-
* values disable the cap. Primary use case:
|
|
161
|
+
* values disable the cap. Primary use case: batch/scripted callers that must
|
|
162
162
|
* bound a single run to keep autonomous execution inside a known envelope.
|
|
163
163
|
*/
|
|
164
164
|
maxSpendUsd?: number;
|
package/dist/commands/start.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StreamTurnDone } from '../agent/types.js';
|
|
1
2
|
interface StartOptions {
|
|
2
3
|
model?: string;
|
|
3
4
|
debug?: boolean;
|
|
@@ -9,8 +10,9 @@ interface StartOptions {
|
|
|
9
10
|
continue?: boolean;
|
|
10
11
|
/** Hard USD cap on total session spend. Stops the loop when exceeded. */
|
|
11
12
|
maxSpend?: string | number;
|
|
12
|
-
/** Run a single prompt non-interactively, then exit. For
|
|
13
|
+
/** Run a single prompt non-interactively, then exit. For batch/scripted use. */
|
|
13
14
|
prompt?: string;
|
|
14
15
|
}
|
|
15
16
|
export declare function startCommand(options: StartOptions): Promise<void>;
|
|
17
|
+
export declare function oneShotExitCodeForTurnReason(reason: StreamTurnDone['reason']): number;
|
|
16
18
|
export {};
|
package/dist/commands/start.js
CHANGED
|
@@ -76,7 +76,7 @@ export async function startCommand(options) {
|
|
|
76
76
|
}
|
|
77
77
|
const workDir = process.cwd();
|
|
78
78
|
// --prompt batch mode: skip all interactive startup UI/side effects so
|
|
79
|
-
// stdout stays clean for
|
|
79
|
+
// stdout stays clean for scripts and one-shot callers. Keep the capability surface to the
|
|
80
80
|
// built-ins only — no panel, no MCP autoconnect, no wallet/banner chatter.
|
|
81
81
|
if (options.prompt) {
|
|
82
82
|
if (options.resume === true || options.resume === 'picker') {
|
|
@@ -282,7 +282,7 @@ export async function startCommand(options) {
|
|
|
282
282
|
workingDir: workDir,
|
|
283
283
|
// Non-TTY (piped) input = scripted mode → trust all tools automatically.
|
|
284
284
|
// Interactive TTY = default mode (prompts for Bash/Write/Edit).
|
|
285
|
-
// --prompt is also scripted;
|
|
285
|
+
// --prompt is also scripted; batch callers never see a TTY.
|
|
286
286
|
permissionMode: (options.trust || options.prompt || !process.stdin.isTTY) ? 'trust' : 'default',
|
|
287
287
|
debug: options.debug,
|
|
288
288
|
resumeSessionId,
|
|
@@ -309,8 +309,12 @@ export async function startCommand(options) {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
// ─── One-shot mode (franklin --prompt "...") ──────────────────────────────
|
|
312
|
-
// Used by
|
|
313
|
-
// they stream, honors --max-spend, exits
|
|
312
|
+
// Used by batch/scripted callers. Non-interactive, prints text deltas to
|
|
313
|
+
// stdout as they stream, honors --max-spend, exits non-zero for any
|
|
314
|
+
// non-completed terminal state.
|
|
315
|
+
export function oneShotExitCodeForTurnReason(reason) {
|
|
316
|
+
return reason === 'completed' ? 0 : 1;
|
|
317
|
+
}
|
|
314
318
|
async function runOneShot(agentConfig, prompt) {
|
|
315
319
|
let delivered = false;
|
|
316
320
|
let exitCode = 0;
|
|
@@ -325,8 +329,7 @@ async function runOneShot(agentConfig, prompt) {
|
|
|
325
329
|
process.stdout.write(event.text);
|
|
326
330
|
}
|
|
327
331
|
else if (event.kind === 'turn_done') {
|
|
328
|
-
|
|
329
|
-
exitCode = 1;
|
|
332
|
+
exitCode = oneShotExitCodeForTurnReason(event.reason);
|
|
330
333
|
process.stdout.write('\n');
|
|
331
334
|
}
|
|
332
335
|
});
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ program
|
|
|
44
44
|
.option('-r, --resume [sessionId]', 'Resume a session by ID (or show picker if omitted)')
|
|
45
45
|
.option('-c, --continue', 'Continue the most recent session in this directory')
|
|
46
46
|
.option('--max-spend <usd>', 'Hard USD cap on total session API spend — session stops when exceeded')
|
|
47
|
-
.option('-p, --prompt <text>', 'Run a single prompt non-interactively (for
|
|
47
|
+
.option('-p, --prompt <text>', 'Run a single prompt non-interactively (for batch/scripted use)')
|
|
48
48
|
.action((options) => startCommand({ ...options, version }));
|
|
49
49
|
program
|
|
50
50
|
.command('resume [sessionId]')
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool visibility categories.
|
|
3
3
|
*
|
|
4
|
-
* Franklin ships with ~27 capabilities. Exposing all of them
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Franklin ships with ~27 capabilities. Exposing all of them on every turn
|
|
5
|
+
* makes the tool inventory large enough that weak models start hallucinating
|
|
6
|
+
* tool names or emitting role-play "[TOOLCALL]" fragments. The compromise:
|
|
7
|
+
* keep the hero surface always-on (file/shell/search PLUS the trading and
|
|
8
|
+
* research tools that define Franklin's category), and gate the long tail
|
|
9
|
+
* (webhook, imagegen, videogen, musicgen, memory, etc.) behind an
|
|
10
|
+
* `ActivateTool` meta-tool the agent pulls on demand.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* History: earlier releases kept only file/shell/search in core, which made
|
|
13
|
+
* mid-tier models answer stock / market questions from 2022 training data
|
|
14
|
+
* instead of calling TradingMarket. That's anti-positioning for an agent
|
|
15
|
+
* whose whole brand is "spends USDC for real market data." Hero tools now
|
|
16
|
+
* live in the always-on set so the default experience shows the wallet
|
|
17
|
+
* actually at work.
|
|
13
18
|
*/
|
|
14
19
|
export declare const CORE_TOOL_NAMES: ReadonlySet<string>;
|
|
15
20
|
/** True if this tool is always available without activation. */
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool visibility categories.
|
|
3
3
|
*
|
|
4
|
-
* Franklin ships with ~27 capabilities. Exposing all of them
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Franklin ships with ~27 capabilities. Exposing all of them on every turn
|
|
5
|
+
* makes the tool inventory large enough that weak models start hallucinating
|
|
6
|
+
* tool names or emitting role-play "[TOOLCALL]" fragments. The compromise:
|
|
7
|
+
* keep the hero surface always-on (file/shell/search PLUS the trading and
|
|
8
|
+
* research tools that define Franklin's category), and gate the long tail
|
|
9
|
+
* (webhook, imagegen, videogen, musicgen, memory, etc.) behind an
|
|
10
|
+
* `ActivateTool` meta-tool the agent pulls on demand.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* History: earlier releases kept only file/shell/search in core, which made
|
|
13
|
+
* mid-tier models answer stock / market questions from 2022 training data
|
|
14
|
+
* instead of calling TradingMarket. That's anti-positioning for an agent
|
|
15
|
+
* whose whole brand is "spends USDC for real market data." Hero tools now
|
|
16
|
+
* live in the always-on set so the default experience shows the wallet
|
|
17
|
+
* actually at work.
|
|
13
18
|
*/
|
|
14
19
|
export const CORE_TOOL_NAMES = new Set([
|
|
15
20
|
// File operations — nothing else works without these.
|
|
@@ -27,8 +32,24 @@ export const CORE_TOOL_NAMES = new Set([
|
|
|
27
32
|
// so keeping this in the core doesn't leak the full inventory.
|
|
28
33
|
'Task',
|
|
29
34
|
// The meta-tool itself — must always be callable so the agent can
|
|
30
|
-
// discover and activate
|
|
35
|
+
// discover and activate anything not in this core set.
|
|
31
36
|
'ActivateTool',
|
|
37
|
+
// ── Hero surface: Franklin's reason to exist ────────────────────────
|
|
38
|
+
// Trading market data — crypto, FX, commodity, stocks (via x402).
|
|
39
|
+
// "Is NVDA up?" / "Should I sell CRCL?" must never fall back to
|
|
40
|
+
// training-data guessing.
|
|
41
|
+
'TradingMarket',
|
|
42
|
+
'TradingSignal',
|
|
43
|
+
// Research — synthesized answers with real citations, semantic web
|
|
44
|
+
// search, and clean URL fetching. Any factual current-events question
|
|
45
|
+
// ("why did X drop?") should route here rather than the model's prior.
|
|
46
|
+
'ExaAnswer',
|
|
47
|
+
'ExaSearch',
|
|
48
|
+
'ExaReadUrls',
|
|
49
|
+
// Plain web fetch — specific URL → readable text. Cheap and obvious
|
|
50
|
+
// enough that every model tends to pick it correctly.
|
|
51
|
+
'WebFetch',
|
|
52
|
+
'WebSearch',
|
|
32
53
|
]);
|
|
33
54
|
/** True if this tool is always available without activation. */
|
|
34
55
|
export function isCoreTool(name) {
|
package/package.json
CHANGED