@blockrun/franklin 3.15.31 → 3.15.32

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.
@@ -7,6 +7,7 @@ import { mkdirSync, writeFileSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { recordFailure } from '../stats/failures.js';
9
9
  import { BLOCKRUN_DIR } from '../config.js';
10
+ import { logger } from '../logger.js';
10
11
  /** Persist a large tool result to disk and return a preview string. */
11
12
  const PERSIST_THRESHOLD = 50_000;
12
13
  const PREVIEW_SIZE = 2_000;
@@ -205,8 +206,22 @@ export class StreamingExecutor {
205
206
  }
206
207
  }
207
208
  }
209
+ // Track elapsed for slow-tool forensics. Verified 2026-05-04
210
+ // from a real session: a 337.6s Bash error left no trace in
211
+ // franklin-debug.log — the user could see the ✗ in the UI but
212
+ // had no post-hoc way to ask "which Bash took 5+ minutes
213
+ // yesterday and what did it run". 30s threshold is conservative
214
+ // (Read/Glob/Grep finish in <1s; only network or shell work
215
+ // crosses).
216
+ const execStart = Date.now();
208
217
  let result = await handler.execute(invocation.input, progressScope);
209
218
  this.guard?.afterExecute(invocation, result);
219
+ const execElapsed = Date.now() - execStart;
220
+ if (execElapsed >= 30_000) {
221
+ const status = result.isError ? 'error' : 'ok';
222
+ const preview = this.inputPreview(invocation) || '';
223
+ logger.info(`[franklin] Slow tool: ${invocation.name} ${status} after ${(execElapsed / 1000).toFixed(1)}s${preview ? ` — ${preview.slice(0, 80)}` : ''}`);
224
+ }
210
225
  // Persist large results to disk with preview.
211
226
  // Instead of just truncating, save the full result to disk so it can be re-read later.
212
227
  if (result.output.length > PERSIST_THRESHOLD) {
@@ -219,15 +234,22 @@ export class StreamingExecutor {
219
234
  }
220
235
  catch (err) {
221
236
  this.guard?.cancelInvocation(invocation.id);
237
+ const errMsg = err.message;
222
238
  recordFailure({
223
239
  timestamp: Date.now(),
224
240
  model: '', // not available at tool level
225
241
  failureType: 'tool_error',
226
242
  toolName: invocation.name,
227
- errorMessage: err.message,
243
+ errorMessage: errMsg,
228
244
  });
245
+ // Always log thrown tool errors. Pre-3.15.32 these went only to
246
+ // failures.jsonl (which the user rarely opens) and were absent
247
+ // from franklin-debug.log entirely. Now `franklin logs` shows
248
+ // them alongside everything else.
249
+ const preview = this.inputPreview(invocation) || '';
250
+ logger.warn(`[franklin] Tool error: ${invocation.name} threw "${errMsg.slice(0, 120)}"${preview ? ` — ${preview.slice(0, 80)}` : ''}`);
229
251
  return {
230
- output: `Error executing ${invocation.name}: ${err.message}`,
252
+ output: `Error executing ${invocation.name}: ${errMsg}`,
231
253
  isError: true,
232
254
  };
233
255
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.31",
3
+ "version": "3.15.32",
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": {