@blockrun/franklin 3.15.34 → 3.15.35

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.
@@ -431,6 +431,11 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
431
431
  updateSessionMeta(sessionId, {
432
432
  model: config.model,
433
433
  workDir,
434
+ // Pin the session's chain so `franklin --resume` can restore it
435
+ // even after `franklin <chain>` shortcuts mutate the persisted
436
+ // default. updateSessionMeta treats this field as sticky once
437
+ // recorded — see storage.ts.
438
+ chain: config.chain,
434
439
  turnCount,
435
440
  messageCount: history.length,
436
441
  inputTokens: sessionInputTokens,
@@ -46,7 +46,28 @@ export async function startCommand(options) {
46
46
  const { findLatestSessionForDir } = await import('../ui/session-picker.js');
47
47
  continueResolvedId = findLatestSessionForDir(process.cwd())?.id;
48
48
  }
49
- const chain = loadChain();
49
+ // Sessions are wallet-bound: the conversation, audit trail, and tool
50
+ // results live on whichever chain the session was started on. If
51
+ // we're resuming, prefer the session's recorded chain over the
52
+ // persisted default — `franklin solana` / `franklin base` shortcuts
53
+ // mutate that default, and a debug invocation between restarts
54
+ // shouldn't be able to silently move the user to a different wallet.
55
+ // Only sessions created in 3.15.35+ have the field; older sessions
56
+ // fall back to the persisted default (matches pre-3.15.35 behavior).
57
+ let chain = loadChain();
58
+ const resumeIdEarly = (typeof options.resume === 'string' && options.resume !== 'picker') ? options.resume
59
+ : continueResolvedId;
60
+ if (resumeIdEarly) {
61
+ const { loadSessionMeta } = await import('../session/storage.js');
62
+ const sessMeta = loadSessionMeta(resumeIdEarly);
63
+ if (sessMeta?.chain && sessMeta.chain !== chain) {
64
+ console.log(chalk.dim(` Restoring session's chain: ${sessMeta.chain} (default was ${chain}; session is wallet-bound to ${sessMeta.chain})`));
65
+ chain = sessMeta.chain;
66
+ }
67
+ else if (sessMeta?.chain) {
68
+ chain = sessMeta.chain;
69
+ }
70
+ }
50
71
  const apiUrl = API_URLS[chain];
51
72
  const config = loadConfig();
52
73
  // Resolve model. Priority: explicit --model > resumed session's model > user
@@ -13,6 +13,18 @@ export interface SessionMeta {
13
13
  updatedAt: number;
14
14
  turnCount: number;
15
15
  messageCount: number;
16
+ /**
17
+ * Chain (`base` | `solana`) the session was started on. Captured at
18
+ * session creation so `franklin --resume` can restore the same chain
19
+ * even if the user later changed their default via
20
+ * `franklin solana` / `franklin base`. Verified 2026-05-04: a debug
21
+ * invocation flipped `~/.blockrun/.chain` to `solana`; the next
22
+ * `--resume` silently moved the user from their funded Base wallet
23
+ * to an underfunded Solana wallet. Sessions are wallet-bound by
24
+ * conversation context — switching chains mid-resume is a bug.
25
+ * Optional for back-compat with pre-3.15.35 sessions.
26
+ */
27
+ chain?: 'base' | 'solana';
16
28
  inputTokens?: number;
17
29
  outputTokens?: number;
18
30
  costUsd?: number;
@@ -113,6 +113,12 @@ export function updateSessionMeta(sessionId, meta) {
113
113
  ...(meta.channel !== undefined || existing?.channel !== undefined
114
114
  ? { channel: meta.channel ?? existing?.channel }
115
115
  : {}),
116
+ // Chain (base / solana) is sticky once set. We never let a later
117
+ // update overwrite an existing value with undefined — that would
118
+ // silently drop the bind-to-original-chain guarantee.
119
+ ...(meta.chain !== undefined || existing?.chain !== undefined
120
+ ? { chain: existing?.chain ?? meta.chain }
121
+ : {}),
116
122
  ...(meta.toolCallCounts !== undefined || existing?.toolCallCounts !== undefined
117
123
  ? { toolCallCounts: meta.toolCallCounts ?? existing?.toolCallCounts }
118
124
  : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.34",
3
+ "version": "3.15.35",
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": {