@blockrun/franklin 3.16.1 → 3.16.3

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.
@@ -802,7 +802,7 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
802
802
  let turnToolCalls = 0; // Total tool calls this user turn
803
803
  const turnToolCounts = new Map(); // Per-tool-name counts this turn
804
804
  const readFileCache = new Set(); // Files already read (dedup)
805
- const MAX_TOOL_CALLS_PER_TURN = 25; // Hard cap per user turn
805
+ const MAX_TOOL_CALLS_PER_TURN = 40; // Soft cap — model gets a stop nudge but can finish. Raised from 25 (3.16.2): real exploratory work routinely needs 25-35 distinct calls, and the soft cap was firing on legit sessions multiple times per day.
806
806
  // Hard break threshold for runaways. The cap above is soft — we
807
807
  // inject a "limit reached" tool_result once and let the model
808
808
  // close out. If it ignores that signal and keeps calling tools,
@@ -36,7 +36,20 @@ async function execute(input, _ctx) {
36
36
  const searchUrl = `https://x.com/search?q=${encodeURIComponent(search_query)}&src=typed_query&f=live`;
37
37
  await browser.open(searchUrl);
38
38
  await browser.waitForTimeout(3500);
39
- const tree = await browser.snapshot();
39
+ // Same defensive guard as SearchX — Playwright can drop the page out
40
+ // from under us during the wait. Surface a useful hint instead of
41
+ // "Cannot read properties of undefined (reading 'snapshot')".
42
+ let tree;
43
+ try {
44
+ tree = await browser.snapshot();
45
+ }
46
+ catch (snapErr) {
47
+ const snapMsg = snapErr instanceof Error ? snapErr.message : String(snapErr);
48
+ return {
49
+ output: `PostToX: Page snapshot failed (${snapMsg.slice(0, 100)}). The browser session likely closed mid-flight — retry, or run \`franklin social setup\` to refresh.`,
50
+ isError: true,
51
+ };
52
+ }
40
53
  // ── Find the article matching the given pre_key ──────────────────
41
54
  const articles = extractArticleBlocks(tree);
42
55
  let matchedTimeRef = null;
@@ -122,7 +122,22 @@ async function execute(input, _ctx) {
122
122
  return { output: `SearchX: Failed to open X.com: ${msg.slice(0, 200)}`, isError: true };
123
123
  }
124
124
  await browser.waitForTimeout(4000);
125
- const tree = await browser.snapshot();
125
+ // Defensive: snapshot() has historically thrown "Cannot read properties
126
+ // of undefined (reading 'snapshot')" when Playwright's underlying page
127
+ // closes between waitForTimeout and the snapshot call (verified in
128
+ // failures.jsonl 2026-04-20). Convert the cryptic error into a useful
129
+ // hint instead of leaking it into the audit log unchanged.
130
+ let tree;
131
+ try {
132
+ tree = await browser.snapshot();
133
+ }
134
+ catch (snapErr) {
135
+ const snapMsg = snapErr instanceof Error ? snapErr.message : String(snapErr);
136
+ return {
137
+ output: `SearchX: Page snapshot failed (${snapMsg.slice(0, 100)}). The browser session likely closed mid-flight — retry, or run \`franklin social setup\` to refresh.`,
138
+ isError: true,
139
+ };
140
+ }
126
141
  // ── Diagnose page state ───────────────────────────────────────────
127
142
  const isLoginWall = tree.includes('Sign in') && tree.includes('Create account');
128
143
  const isRateLimit = tree.includes('Rate limit') || tree.includes('Something went wrong');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.16.1",
3
+ "version": "3.16.3",
4
4
  "description": "Franklin Agent — 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": {