@blockrun/franklin 3.15.63 → 3.15.65

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.
@@ -6,6 +6,8 @@ import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { execSync } from 'node:child_process';
8
8
  import { getWalletAddress as getBaseWalletAddress } from '@blockrun/llm';
9
+ import { Keypair } from '@solana/web3.js';
10
+ import bs58 from 'bs58';
9
11
  import { loadLearnings, decayLearnings, saveLearnings, formatForPrompt, loadSkills, formatSkillsForPrompt } from '../learnings/store.js';
10
12
  // ─── System Instructions Assembly ──────────────────────────────────────────
11
13
  // Composable prompt sections — each independently maintainable and conditionally includable.
@@ -164,10 +166,16 @@ Franklin stores wallet keys in ~/.blockrun/. When the user asks about wallet loc
164
166
  Format: 66-char hex string starting with 0x (file name intentionally looks like a session token for obscurity)
165
167
  Address: derivable from the key; also available via getWalletAddress() from @blockrun/llm
166
168
  - Solana wallet:
167
- File: ~/.blockrun/solana-wallet.json (JSON with address + private_key)
168
- - Chain selection: ~/.blockrun/.chain ("base" or "solana")
169
- - Spending tracker: ~/.blockrun/spending.json
170
- - Programmatic access: import { getWalletAddress, getOrCreateWallet } from '@blockrun/llm'
169
+ Private key file: ~/.blockrun/.solana-session
170
+ Format: bare base58 secret key (file name mirrors the Base wallet's obscurity convention; mode 600)
171
+ Address: derivable; available via getOrCreateSolanaWallet() from @blockrun/llm
172
+ - Chain selection: ~/.blockrun/payment-chain ("base" or "solana"). Legacy file ~/.blockrun/.chain may also exist on installs that haven't migrated; canonical is payment-chain.
173
+ - Spending data:
174
+ - ~/.blockrun/franklin-stats.json — rolling totals + per-model breakdown (what \`franklin stats\` reads).
175
+ - ~/.blockrun/franklin-audit.jsonl — append-only forensic ledger of every LLM call.
176
+ - ~/.blockrun/cost_log.jsonl — append-only ledger of every settled x402 payment (written by @blockrun/llm SDK).
177
+ - Use \`franklin stats\` / \`franklin content list\` instead of parsing files when the user asks "how much did I spend".
178
+ - Programmatic access: import { getWalletAddress, getOrCreateWallet, getOrCreateSolanaWallet } from '@blockrun/llm'
171
179
 
172
180
  When the user asks about "my wallet" without qualifier, default to Base (it's the primary chain shown at launch). Only mention Solana if the chain file says solana or the user explicitly asks.`;
173
181
  }
@@ -184,7 +192,7 @@ You run on the BlockRun AI Gateway. When the user asks you to "test the BlockRun
184
192
  - \`GET /.well-known/x402\` — x402 resource list with prices
185
193
 
186
194
  **LLM (POST, x402-paid)**
187
- - \`POST /v1/chat/completions\` — OpenAI-compatible. Body: \`{ model, messages, stream?, tools?, max_tokens?, temperature? }\`. \`model\` MUST come from \`GET /v1/models\` (e.g. \`anthropic/claude-sonnet-4.6\`, \`openai/gpt-5.1\`, \`xai/grok-5\`). Wrong model name 400 with the valid list in the error body.
195
+ - \`POST /v1/chat/completions\` — OpenAI-compatible. Body: \`{ model, messages, stream?, tools?, max_tokens?, temperature? }\`. \`model\` MUST come from \`GET /v1/models\` (real frontier examples on the gateway as of 2026-05: \`anthropic/claude-sonnet-4.6\`, \`anthropic/claude-opus-4.7\`, \`deepseek/deepseek-v4-pro\`, \`zai/glm-5.1\`, \`nvidia/qwen3-coder-480b\`, \`openai/gpt-5-nano\`). Do NOT invent versions like \`openai/gpt-5.1\` or \`xai/grok-5\` those don't exist; the gateway 400s with the valid list in the error body, so when in doubt fetch \`GET /v1/models\` first.
188
196
  - \`POST /v1/messages\` — Anthropic-compatible. Body: \`{ model, messages, max_tokens, system?, tools? }\`.
189
197
 
190
198
  **Media (POST, x402-paid; GET to poll async jobs)**
@@ -604,7 +612,7 @@ function buildEnvironmentSection(workingDir) {
604
612
  if (wallet.base)
605
613
  lines.push(`- Base wallet address: ${wallet.base} (private key at ~/.blockrun/.session)`);
606
614
  if (wallet.solana)
607
- lines.push(`- Solana wallet address: ${wallet.solana} (private key at ~/.blockrun/solana-wallet.json)`);
615
+ lines.push(`- Solana wallet address: ${wallet.solana} (private key at ~/.blockrun/.solana-session)`);
608
616
  }
609
617
  return lines.join('\n');
610
618
  }
@@ -644,14 +652,42 @@ function readRuntimeWallet() {
644
652
  out.base = addr;
645
653
  }
646
654
  catch { /* SDK may not be available in all contexts — skip silently */ }
647
- // Solana address: read from JSON
655
+ // Solana address: prefer the canonical SDK file `.solana-session`
656
+ // (raw base58 secret key, mode 600 — what the SDK actually writes
657
+ // and reads via getOrCreateSolanaWallet). Fall back to the legacy
658
+ // `solana-wallet.json` shape (JSON with {address, privateKey}) for
659
+ // unmigrated installs. Verified 2026-05-05: user's machine had
660
+ // both files present — `.solana-session` (88 bytes) was canonical
661
+ // and `solana-wallet.json` (123 bytes) was a leftover from an
662
+ // earlier SDK version. The pre-fix code only read the legacy file,
663
+ // so once a user `rm`s it after migration, the runtime-wallet
664
+ // section silently stops showing the Solana address.
648
665
  try {
649
- const solPath = path.join(blockrunDir, 'solana-wallet.json');
650
- if (fs.existsSync(solPath)) {
651
- const data = JSON.parse(fs.readFileSync(solPath, 'utf-8'));
652
- const addr = data.address || data.publicKey;
653
- if (addr && typeof addr === 'string')
654
- out.solana = addr;
666
+ const canonical = path.join(blockrunDir, '.solana-session');
667
+ if (fs.existsSync(canonical)) {
668
+ const key = fs.readFileSync(canonical, 'utf-8').trim();
669
+ if (key) {
670
+ // Derive the public address from the secret key. Same primitives
671
+ // jupiter.ts:229 uses for transaction signing — keeps this
672
+ // sync-with-SDK without depending on async `getOrCreateSolanaWallet`
673
+ // (which would create a wallet on first read, an unwanted side
674
+ // effect for a context-builder).
675
+ try {
676
+ const bytes = bs58.decode(key);
677
+ const kp = Keypair.fromSecretKey(bytes);
678
+ out.solana = kp.publicKey.toBase58();
679
+ }
680
+ catch { /* derivation failed — fall through to legacy probe */ }
681
+ }
682
+ }
683
+ if (!out.solana) {
684
+ const legacy = path.join(blockrunDir, 'solana-wallet.json');
685
+ if (fs.existsSync(legacy)) {
686
+ const data = JSON.parse(fs.readFileSync(legacy, 'utf-8'));
687
+ const addr = data.address || data.publicKey;
688
+ if (addr && typeof addr === 'string')
689
+ out.solana = addr;
690
+ }
655
691
  }
656
692
  }
657
693
  catch { /* ignore */ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.63",
3
+ "version": "3.15.65",
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": {