@blockrun/franklin 3.15.76 → 3.15.77

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.
@@ -52,6 +52,23 @@ export interface LLMClientOptions {
52
52
  chain: Chain;
53
53
  debug?: boolean;
54
54
  }
55
+ /**
56
+ * Replace Unicode box-drawing characters with their ASCII equivalents.
57
+ *
58
+ * Models occasionally emit U+2502 (`│`) and U+2500 (`─`) in markdown tables
59
+ * — sometimes mixed with ASCII `|` / `-` in the same table. No markdown
60
+ * renderer parses the mix, and the "table" displays as run-on text. Verified
61
+ * 2026-05-06 in a real session: opus-4.7 emitted a CRCL fundamentals table
62
+ * with `│` data rows and `|` separator, ignoring the system-prompt nudge
63
+ * added in 3.15.76. The unconditional swap fixes the rendering at the
64
+ * streaming boundary so every downstream surface (user terminal, conversation
65
+ * history, audit log) gets the corrected version.
66
+ *
67
+ * Trade: the rare case where a user genuinely wants box-drawing in output
68
+ * (e.g. asking what U+2502 looks like) loses fidelity. Acceptable — that
69
+ * case has no real-world frequency, the broken-tables case has weekly.
70
+ */
71
+ export declare function sanitizeTableUnicode(s: string): string;
55
72
  /**
56
73
  * Extract the most human-readable message from an error body.
57
74
  * Some gateways wrap provider errors multiple times, e.g.
package/dist/agent/llm.js CHANGED
@@ -28,6 +28,27 @@ function parseTimeoutEnv(name) {
28
28
  const parsed = raw ? Number.parseInt(raw, 10) : NaN;
29
29
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
30
30
  }
31
+ /**
32
+ * Replace Unicode box-drawing characters with their ASCII equivalents.
33
+ *
34
+ * Models occasionally emit U+2502 (`│`) and U+2500 (`─`) in markdown tables
35
+ * — sometimes mixed with ASCII `|` / `-` in the same table. No markdown
36
+ * renderer parses the mix, and the "table" displays as run-on text. Verified
37
+ * 2026-05-06 in a real session: opus-4.7 emitted a CRCL fundamentals table
38
+ * with `│` data rows and `|` separator, ignoring the system-prompt nudge
39
+ * added in 3.15.76. The unconditional swap fixes the rendering at the
40
+ * streaming boundary so every downstream surface (user terminal, conversation
41
+ * history, audit log) gets the corrected version.
42
+ *
43
+ * Trade: the rare case where a user genuinely wants box-drawing in output
44
+ * (e.g. asking what U+2502 looks like) loses fidelity. Acceptable — that
45
+ * case has no real-world frequency, the broken-tables case has weekly.
46
+ */
47
+ export function sanitizeTableUnicode(s) {
48
+ if (!s)
49
+ return s;
50
+ return s.replace(/│/g, '|').replace(/─/g, '-');
51
+ }
31
52
  function getModelRequestTimeoutMs() {
32
53
  // 180s budget for *time-to-headers* (the gateway flushes SSE headers only
33
54
  // once the upstream model emits its first token). Reasoning-class models
@@ -545,6 +566,15 @@ export class ModelClient {
545
566
  const appendText = (text) => {
546
567
  if (!text)
547
568
  return;
569
+ // Sanitize Unicode box-drawing chars to ASCII pipe/dash. 3.15.76's
570
+ // system-prompt nudge asked models not to emit U+2502 / U+2500 in
571
+ // tables — opus-4.7 ignored it 2026-05-06, shipped a CRCL analysis
572
+ // table where data rows used `│` and the separator used `|`. No
573
+ // markdown renderer parses that mix; the table displayed as run-on
574
+ // text. Normalize at the streaming boundary so the user, the model
575
+ // history (next turn the model sees its own corrected output), and
576
+ // the audit log all match.
577
+ text = sanitizeTableUnicode(text);
548
578
  currentText += text;
549
579
  if (textEmission.mode === 'undecided') {
550
580
  const trimmed = currentText.trimStart();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.15.76",
3
+ "version": "3.15.77",
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": {