@blockrun/franklin 3.15.96 → 3.15.97

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.
@@ -219,7 +219,19 @@ export class StreamingExecutor {
219
219
  const execElapsed = Date.now() - execStart;
220
220
  if (execElapsed >= 30_000) {
221
221
  const status = result.isError ? 'error' : 'ok';
222
- const preview = this.inputPreview(invocation) || '';
222
+ // Single-line the preview before logging. Bash invocations like
223
+ // `python3 -c "<heredoc>"` carry embedded newlines that, sliced
224
+ // raw, break the one-line-per-entry contract of
225
+ // franklin-debug.log and shred any parser that splits on
226
+ // `^\[timestamp\]`. Verified 2026-05-04 (and reviewed again
227
+ // 2026-05-12): a real entry produced
228
+ // `Slow tool: Bash ok after 438.4s — cd ... python3 -c "`
229
+ // `import subprocess`
230
+ // `[2026-05-04T19:25:10] [ERROR] Signature-loop hard stop ...`
231
+ // where "import subprocess" sat on its own line, untimestamped,
232
+ // because the preview's first 80 chars contained the heredoc
233
+ // opener.
234
+ const preview = (this.inputPreview(invocation) || '').replace(/[\r\n]+/g, ' ');
223
235
  logger.info(`[franklin] Slow tool: ${invocation.name} ${status} after ${(execElapsed / 1000).toFixed(1)}s${preview ? ` — ${preview.slice(0, 80)}` : ''}`);
224
236
  }
225
237
  // Persist large results to disk with preview.
package/dist/logger.js CHANGED
@@ -86,7 +86,22 @@ function writeFile(level, msg) {
86
86
  writesSinceRotateProbe = 0;
87
87
  maybeRotate();
88
88
  }
89
- const clean = msg.replace(ANSI_RE, '');
89
+ // Two-step sanitize, in this order:
90
+ // 1. Collapse embedded newlines (\n / \r / \r\n) to a literal
91
+ // " ↵ " marker so a single logger call always produces one
92
+ // physical log line.
93
+ // 2. Strip ANSI escape sequences.
94
+ //
95
+ // Order matters: ANSI_RE strips bare \r (used by progress bars), so
96
+ // running it first would erase \r-only line breaks and let
97
+ // "first\rsecond" appear as "firstsecond" in the log. Verified
98
+ // 2026-05-12 from franklin-debug.log: a `Slow tool: Bash ok ...
99
+ // python3 -c "` preview leaked `import subprocess` onto its own
100
+ // untimestamped line because the embedded \n in the bash command
101
+ // survived the preview slice and broke any parser that splits on
102
+ // ^\[timestamp\]. Cheaper to enforce one-line-per-entry here than
103
+ // to police every callsite.
104
+ const clean = msg.replace(/\r\n|\r|\n/g, ' ↵ ').replace(ANSI_RE, '');
90
105
  fs.appendFileSync(LOG_FILE, `[${new Date().toISOString()}] [${level.toUpperCase()}] ${clean}\n`);
91
106
  }
92
107
  catch { /* best-effort — never break the agent on log failure */ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.96",
3
+ "version": "3.15.97",
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": {