@blockrun/franklin 3.6.19 → 3.6.20

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/README.md CHANGED
@@ -27,11 +27,11 @@
27
27
 
28
28
  <p>
29
29
  <a href="#quick-start">Quick&nbsp;start</a> ·
30
- <a href="#a-new-category">New&nbsp;category</a> ·
30
+ <a href="#yopo">YOPO</a> ·
31
+ <a href="#a-new-category">Category</a> ·
31
32
  <a href="#what-franklin-can-execute">What&nbsp;it&nbsp;does</a> ·
32
33
  <a href="#smart-router">Smart&nbsp;Router</a> ·
33
- <a href="#the-comparison">Compare</a> ·
34
- <a href="#features">Features</a> ·
34
+ <a href="#the-comparison">vs.&nbsp;Claude&nbsp;Code</a> ·
35
35
  <a href="#how-it-works">Architecture</a> ·
36
36
  <a href="#community">Community</a>
37
37
  </p>
@@ -42,10 +42,16 @@
42
42
 
43
43
  ## The pitch in one paragraph
44
44
 
45
- `franklin` is not a coding copilot and not just another task runner. Franklin is a **wallet-native economic agent**: software with purchasing power. You give it a goal and a budget. Franklin decides what model to call, what tool to use, what API is worth paying for, and when to stop. Every paid action routes through the [x402](https://x402.org) micropayment protocol and settles against your own wallet. No subscriptions. No API keys. No account. The wallet is the identity.
45
+ Claude Code writes code. Cursor writes code. Franklin writes code **and spends money to get the job done**. It holds a USDC wallet, picks the best model per task from 55+ providers, purchases trading data, generates images, pays for web search — all autonomously. You state an outcome and set a budget. Franklin decides what to call, what to pay for, and when to stop. Every paid action routes through the [x402](https://x402.org) micropayment protocol and settles against your own wallet. No subscriptions. No API keys. No account. The wallet is the identity.
46
46
 
47
47
  Built by the [BlockRun](https://blockrun.ai) team. Apache-2.0. TypeScript. Ships as one npm package.
48
48
 
49
+ > **YOPO — You Only Pay Outcome**
50
+ >
51
+ > Not a subscription (pay for access). Not a generic pay-per-call (pay for trying).
52
+ > You pay only for the work Franklin delivers. Provider cost + 5%, settled per action
53
+ > in USDC. No monthly fees. No rate limits. No overdraft.
54
+
49
55
  ---
50
56
 
51
57
  ## Quick start
@@ -66,24 +72,41 @@ That's it. Zero signup, zero credit card, zero phone verification. Send **$5 of
66
72
 
67
73
  ---
68
74
 
75
+ ## YOPO
76
+
77
+ **You Only Pay Outcome.** This is Franklin's pricing model, and it is the opposite of almost every other AI product you use.
78
+
79
+ | | You pay for... | Result |
80
+ | ----------------------- | -------------------------------------------- | ------------------------------------ |
81
+ | Subscription (ChatGPT Plus, Claude Max, Cursor Pro) | Access. Paid whether you use it or not. | $20–200/month, rate-limited. |
82
+ | Pay-per-call (OpenAI API, etc.) | Every attempt — even failed ones. | Hidden cost from retries, dead ends. |
83
+ | **Franklin (YOPO)** | **The outcome.** Each signed micropayment. | **Provider cost + 5%. No more.** |
84
+
85
+ Three consequences fall out of this:
86
+
87
+ 1. **No subscriptions.** Use Franklin for $0.50 one week and $50 the next — you pay for compute actually consumed, nothing more.
88
+ 2. **No rate limits.** Subscriptions throttle you when you need AI most. YOPO has no artificial caps — if you have USDC, you have access.
89
+ 3. **No overdraft.** The wallet balance IS the hard limit. When it's empty, Franklin stops. No surprise bills, no 3 a.m. rate-limit walls.
90
+
91
+ Concretely — $1 in USDC gets you roughly:
92
+ - ~400K GPT-4o input tokens
93
+ - ~7M DeepSeek tokens
94
+ - ~13M Gemini Flash tokens
95
+ - ~20 DALL-E 3 images
96
+ - ~40 Exa neural web searches
97
+ - Unlimited NVIDIA GPT-OSS (free tier, no wallet needed)
98
+
99
+ ---
100
+
69
101
  ## A new category
70
102
 
71
103
  > **Economic Agent**
72
- >
104
+ >
73
105
  > Software that can hold a wallet, price its own actions, spend toward an outcome, and stop at a hard budget cap.
74
106
 
75
- That definition matters.
107
+ Most AI products fit into one of three buckets: they answer questions, write code, or automate a fixed workflow. None of them can spend money.
76
108
 
77
- Most AI products fit into one of three buckets:
78
- - They answer questions.
79
- - They write code.
80
- - They automate a fixed workflow.
81
-
82
- Franklin does something different. It combines:
83
- - **Purchasing power** — it can pay for models, tools, and APIs.
84
- - **Budget awareness** — it knows spend is part of the problem, not an afterthought.
85
- - **Cross-vertical execution** — marketing, trading, research, code, ops.
86
- - **Hard stopping conditions** — when the wallet or budget says stop, it stops.
109
+ Franklin can. It combines **purchasing power** (it pays for models, tools, and APIs), **budget awareness** (cost is part of the loop, not an afterthought), **cross-vertical execution** (code, trading, research, marketing, ops), and **hard stopping conditions** (wallet balance is a real constraint, not a suggestion).
87
110
 
88
111
  That is why Franklin is an economic agent, not just a task agent.
89
112
 
@@ -165,7 +188,7 @@ After running `franklin social setup && franklin social login x`, Franklin can s
165
188
 
166
189
  Code is still first-class. It is just **one workload**, not the category.
167
190
 
168
- Every tool call is itemized. Every token is priced. When the wallet hits zero, Franklin stops. No overdraft, no surprise bill, no rate-limit wall at 3am.
191
+ Every tool call is itemized. Every token is priced. When the wallet hits zero, Franklin stops. No overdraft, no surprise bill, no rate-limit wall at 3 a.m. — this is YOPO in practice.
169
192
 
170
193
  ---
171
194
 
@@ -221,23 +244,23 @@ The router also learns from **your** usage. If you keep retrying a model for cod
221
244
  <tr>
222
245
  <td width="33%" valign="top">
223
246
 
224
- ### 💳 &nbsp;Budget is native
247
+ ### 💳 &nbsp;AI is utility, not SaaS
225
248
 
226
- Franklin does not bolt spend tracking on afterward. Cost is part of the loop. The agent can choose free, cheap, or premium paths per step, and every paid action settles against your wallet.
249
+ You don't subscribe to electricity, you pay for what you use. Franklin brings the same model to AI. YOPO settlement means Franklin never bills you for access, only for outcomes. No monthly fees, no rate limits, no overdraft.
227
250
 
228
251
  </td>
229
252
  <td width="33%" valign="top">
230
253
 
231
- ### 🔐 &nbsp;Wallet is identity
254
+ ### 🧠 &nbsp;Multi-model is the future
232
255
 
233
- No email. No phone. No KYC. Your Base or Solana address is your account. Portable across machines. Your sessions, your config, your money.
256
+ No single model is best at everything. Claude writes better code, Gemini handles longer context, DeepSeek costs 20x less for simple tasks. The Smart Router routes every request to the optimal model in <1ms — up to 89% savings vs. always using Opus.
234
257
 
235
258
  </td>
236
259
  <td width="33%" valign="top">
237
260
 
238
- ### 🧠 &nbsp;One runtime, many verticals
261
+ ### 🔐 &nbsp;Wallet is identity
239
262
 
240
- Marketing, trading, research, code, and anything else you can express as tools plus budgeted execution. Franklin is a runtime for economic workflows, not a single-purpose copilot.
263
+ No email. No phone. No KYC. Your Base or Solana address is your account — portable, permissionless, global. API keys require US banking and account approval. A wallet requires only USDC.
241
264
 
242
265
  </td>
243
266
  </tr>
@@ -247,17 +270,19 @@ Marketing, trading, research, code, and anything else you can express as tools p
247
270
 
248
271
  ## The comparison
249
272
 
250
- | | Chatbots | Coding agents | Workflow tools | **Franklin** |
251
- | ------------------------------------ | --------------- | ---------------- | ---------------- | ------------------------------- |
252
- | Main unit of value | Answers | Code changes | Fixed automations| **Budgeted outcomes** |
253
- | Has purchasing power | ❌ | ❌ | ❌ | ✅ **wallet-native** |
254
- | Picks best model per task | ❌ | ❌ | ❌ | ✅ **learned router** |
255
- | Can choose tools/models per step | ⚠️ limited | mostly coding | ❌ usually fixed | **yes** |
256
- | Works across marketing/trading/code | ⚠️ | ❌ code-first | ⚠️ integration-bound | **cross-vertical** |
257
- | Hard spend cap | | | ⚠️ external billing | **wallet balance** |
258
- | Identity | Account | Account / API key| Account | **wallet** |
259
- | Start free, no signup | ⚠️ | ❌ / BYOK | ❌ | ✅ |
260
- | Paid APIs through one interface | ❌ | ⚠️ | | ✅ **55+ models + paid tools** |
273
+ | | Claude Code | Cursor | Chatbots | **Franklin** |
274
+ | -------------------------------------- | --------------- | ---------------- | ---------------- | ------------------------------- |
275
+ | Writes code | | | ⚠️ | |
276
+ | **Spends money for you** | ❌ | ❌ | ❌ | ✅ **USDC wallet, x402** |
277
+ | **Buys data + APIs + images + search** | ❌ | ❌ | ❌ | ✅ **55+ APIs, one wallet** |
278
+ | Picks best model per task | Anthropic only | plan-tied | ❌ | **Smart Router, 55+ models** |
279
+ | Pricing model | Subscription | Subscription | Subscription | **YOPO** per outcome, USDC |
280
+ | Monthly fee | $20–$200 | $20–$40 | $20+ | **$0** |
281
+ | Rate-limited | Yes | Yes | Yes | No limited only by wallet |
282
+ | Works when provider goes down | | ❌ | ❌ | ✅ **routes to another** |
283
+ | Identity | Anthropic account | Cursor account | Account / email | ✅ **wallet, no signup** |
284
+ | Start free, no KYC | ❌ | ❌ | ❌ | ✅ |
285
+ | Source | Closed | Closed | Closed | **Apache 2.0, local-first** |
261
286
 
262
287
  **Franklin is the economic agent category in one sentence:** software with a wallet that can spend toward a result.
263
288
 
@@ -281,8 +306,8 @@ Ask "generate a logo" — Franklin calls DALL-E / GPT Image, saves the result lo
281
306
  **🧠 55+ models via one wallet**
282
307
  Anthropic, OpenAI, Google, xAI, DeepSeek, GLM, Kimi, Minimax, NVIDIA free tier. One wallet, one interface, automatic fallback.
283
308
 
284
- **💳 x402 micropayments**
285
- HTTP 402 native. Every paid action is a signed micropayment against your USDC balance. No subscriptions. No refund loop. No account lock-in.
309
+ **💳 x402 micropayments (YOPO)**
310
+ HTTP 402 native. Every paid action is a signed USDC micropayment via EIP-712 — non-custodial, your keys never leave your machine. YOPO: you pay only for outcomes.
286
311
 
287
312
  **🧠 Learned model router**
288
313
  Trained on 2M+ real requests. Classifies your task and picks the best model from 55+ LLMs. Four profiles (auto/eco/premium/free). Adapts to your usage over time.
@@ -480,8 +505,8 @@ Apache-2.0. See [LICENSE](LICENSE).
480
505
 
481
506
  <div align="center">
482
507
 
483
- **Franklin is the economic agent.**<br>
484
- <sub>Your wallet. Your budget. Your results.</sub>
508
+ **The AI agent with a wallet.**<br>
509
+ <sub>YOPO — You Only Pay Outcome. Your wallet. Your budget. Your results.</sub>
485
510
 
486
511
  <br>
487
512
 
@@ -636,7 +636,12 @@ export async function handleSlashCommand(input, ctx) {
636
636
  ctx.config.model = newModel;
637
637
  ctx.config.baseModel = newModel; // Update recovery target so loop doesn't reset
638
638
  ctx.config.onModelChange?.(newModel, 'user');
639
- ctx.onEvent({ kind: 'text_delta', text: `Model **${newModel}**\n` });
639
+ // Warn when switching from free to paid so users know charges start now
640
+ const isFree = (m) => m.startsWith('nvidia/') || m === 'blockrun/free';
641
+ const paidWarning = !isFree(newModel)
642
+ ? ` ⚠️ (paid — charges from your wallet per call)`
643
+ : '';
644
+ ctx.onEvent({ kind: 'text_delta', text: `Model → **${newModel}**${paidWarning}\n` });
640
645
  }
641
646
  emitDone(ctx);
642
647
  return { handled: true };
@@ -748,8 +753,9 @@ export async function handleSlashCommand(input, ctx) {
748
753
  saveSolanaWallet(key);
749
754
  ctx.onEvent({ kind: 'text_delta', text: `**Wallet Imported (Solana)**\n` +
750
755
  ` Address: ${address}\n` +
751
- ` Saved to: ~/.blockrun/\n\n` +
752
- `Restart Franklin to use the new wallet.\n`
756
+ ` Saved to: ~/.blockrun/solana-wallet.json\n\n` +
757
+ `⚠️ IMPORTANT: This session is still using the OLD wallet.\n` +
758
+ ` Run \`/exit\` now, then restart \`franklin\` to use the new wallet.\n`
753
759
  });
754
760
  }
755
761
  else {
@@ -759,8 +765,9 @@ export async function handleSlashCommand(input, ctx) {
759
765
  saveWallet(key);
760
766
  ctx.onEvent({ kind: 'text_delta', text: `**Wallet Imported (Base)**\n` +
761
767
  ` Address: ${account.address}\n` +
762
- ` Saved to: ~/.blockrun/\n\n` +
763
- `Restart Franklin to use the new wallet.\n`
768
+ ` Saved to: ~/.blockrun/wallet.json\n\n` +
769
+ `⚠️ IMPORTANT: This session is still using the OLD wallet.\n` +
770
+ ` Run \`/exit\` now, then restart \`franklin\` to use the new wallet.\n`
764
771
  });
765
772
  }
766
773
  }
@@ -256,6 +256,16 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
256
256
  };
257
257
  pruneOldSessions(sessionId); // Cleanup old sessions on start, protect current
258
258
  persistSessionMeta();
259
+ // Flush session meta on SIGINT/SIGTERM so mid-stream Ctrl+C doesn't
260
+ // leave a stale .meta.json (wrong turnCount/messageCount/cost).
261
+ const exitFlush = () => {
262
+ try {
263
+ persistSessionMeta();
264
+ }
265
+ catch { /* best effort */ }
266
+ };
267
+ process.once('SIGINT', exitFlush);
268
+ process.once('SIGTERM', exitFlush);
259
269
  while (true) {
260
270
  let input = await getUserInput();
261
271
  if (input === null)
@@ -18,11 +18,21 @@ function readPid() {
18
18
  function isRunning(pid) {
19
19
  try {
20
20
  process.kill(pid, 0);
21
- return true;
22
21
  }
23
22
  catch {
24
23
  return false;
25
24
  }
25
+ // PID may have been recycled to an unrelated process. Confirm the
26
+ // command line actually looks like Franklin before trusting the PID.
27
+ try {
28
+ const { execSync } = require('node:child_process');
29
+ const cmd = execSync(`ps -p ${pid} -o command=`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
30
+ return /franklin|runcode|node.*dist\/index/.test(cmd);
31
+ }
32
+ catch {
33
+ // ps failed — fall back to assuming PID is ours (conservative, avoids false "running")
34
+ return true;
35
+ }
26
36
  }
27
37
  export async function daemonCommand(action, options) {
28
38
  const port = parseInt(options.port || String(DEFAULT_PROXY_PORT));
@@ -34,7 +44,7 @@ export async function daemonCommand(action, options) {
34
44
  case 'start': {
35
45
  const existing = readPid();
36
46
  if (existing && isRunning(existing)) {
37
- console.log(chalk.yellow(`runcode daemon already running (PID ${existing})`));
47
+ console.log(chalk.yellow(`franklin daemon already running (PID ${existing})`));
38
48
  console.log(chalk.dim(` Proxy: http://localhost:${port}/api`));
39
49
  return;
40
50
  }
@@ -5,18 +5,19 @@ import chalk from 'chalk';
5
5
  import { createPanelServer } from '../panel/server.js';
6
6
  export async function panelCommand(options) {
7
7
  const requestedPort = parseInt(options.port || '3100', 10);
8
- // Handle port-in-use by trying up to 10 subsequent ports.
8
+ // Handle port-in-use by trying up to 20 subsequent ports.
9
+ const MAX_ATTEMPTS = 20;
9
10
  const tryListen = (port, attempt) => {
10
11
  const server = createPanelServer(port);
11
12
  server.on('error', (err) => {
12
- if (err.code === 'EADDRINUSE' && attempt < 10) {
13
+ if (err.code === 'EADDRINUSE' && attempt < MAX_ATTEMPTS) {
13
14
  console.log(chalk.yellow(` Port ${port} busy — trying ${port + 1}...`));
14
15
  tryListen(port + 1, attempt + 1);
15
16
  return;
16
17
  }
17
18
  console.error(chalk.red(`\n Panel failed to start: ${err.message}`));
18
19
  if (err.code === 'EADDRINUSE') {
19
- console.error(chalk.dim(` All ports from ${requestedPort} to ${requestedPort + 9} are busy.`));
20
+ console.error(chalk.dim(` All ports from ${requestedPort} to ${requestedPort + MAX_ATTEMPTS - 1} are busy.`));
20
21
  console.error(chalk.dim(` Try: franklin panel --port 4000`));
21
22
  }
22
23
  process.exit(1);
@@ -21,8 +21,16 @@ async function execute(input, ctx) {
21
21
  const subModel = model || registeredParentModel || 'nvidia/nemotron-ultra-253b';
22
22
  // Cost gate: if parent is free but sub-agent wants paid, ask user first.
23
23
  // Prevents silent charges when the agent decides to spawn a more capable sub-agent.
24
- if (isFreeModel(registeredParentModel) && !isFreeModel(subModel) && ctx.onAskUser) {
24
+ if (isFreeModel(registeredParentModel) && !isFreeModel(subModel)) {
25
25
  const shortLabel = subModel.split('/').pop() || subModel;
26
+ if (!ctx.onAskUser) {
27
+ // No way to prompt the user (daemon/panel/non-interactive mode).
28
+ // Fail closed — refuse the paid spawn rather than silently charging.
29
+ return {
30
+ output: `Sub-agent declined: parent is on a free model but sub-agent requested a paid model (${shortLabel}). No interactive prompt available. Retry with model='nemotron' or run interactively to approve.`,
31
+ isError: true,
32
+ };
33
+ }
26
34
  const answer = await ctx.onAskUser(`Sub-agent wants to use ${shortLabel} (paid). Approve?`, ['y', 'n']);
27
35
  if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
28
36
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.6.19",
3
+ "version": "3.6.20",
4
4
  "description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
5
5
  "type": "module",
6
6
  "exports": {