@blockrun/franklin 3.15.70 → 3.15.72
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.
- package/dist/agent/context.js +3 -2
- package/dist/agent/loop.js +12 -2
- package/dist/stats/audit.js +12 -1
- package/dist/tools/prediction.d.ts +2 -8
- package/dist/tools/prediction.js +84 -26
- package/package.json +1 -1
package/dist/agent/context.js
CHANGED
|
@@ -336,13 +336,14 @@ Your training data is frozen in the past. Live-world questions MUST be answered
|
|
|
336
336
|
|
|
337
337
|
If you find yourself about to emit one of these, stop and call the tool instead. If you don't know which ticker the user means, call ExaSearch or AskUser — never deflect.
|
|
338
338
|
|
|
339
|
-
**Prediction markets (PredictionMarket).** When the user asks about real-world odds — elections, "will X happen by year-end", "Polymarket on Y", "Kalshi market for Z", "what are the odds of recession" — use **PredictionMarket** instead of guessing.
|
|
339
|
+
**Prediction markets (PredictionMarket).** When the user asks about real-world odds — elections, "will X happen by year-end", "Polymarket on Y", "Kalshi market for Z", "what are the odds of recession" — use **PredictionMarket** instead of guessing. Eight actions, route by intent:
|
|
340
340
|
- "is there a market on X anywhere?" / unknown which platform → \`searchAll\` (\$0.005) — single call across Polymarket+Kalshi+Limitless+Opinion+Predict.Fun.
|
|
341
341
|
- "what are the odds on Polymarket / Kalshi specifically" → \`searchPolymarket\` (\$0.001) and \`searchKalshi\` (\$0.001) **in parallel**; comparing implied probability across the two venues is the high-value answer.
|
|
342
342
|
- "where do Polymarket and Kalshi disagree / arbitrage" → \`crossPlatform\` (\$0.005) returns pre-matched pairs.
|
|
343
343
|
- "who's profitable / top traders / who should I follow on Polymarket" → \`leaderboard\` (\$0.001) — global top wallets by P&L.
|
|
344
344
|
- "how is wallet 0xabc doing / show this trader's P&L / are they profitable" → \`walletProfile\` (\$0.005) with \`wallets="<address>"\` (comma-separated for batch).
|
|
345
|
-
- "what are smart traders betting on right now / smart money flow" → \`smartActivity\` (\$0.005) — markets where high-P&L wallets are positioning.
|
|
345
|
+
- "what are smart traders betting on right now / smart money flow across markets" → \`smartActivity\` (\$0.005) — markets where high-P&L wallets are positioning.
|
|
346
|
+
- "show smart money on this specific Polymarket market / this condition_id" → \`smartMoney\` (\$0.005) with \`conditionId="<condition_id>"\`.
|
|
346
347
|
|
|
347
348
|
NEVER answer "what are the odds of X" from training-data memory — these are live markets that move every minute. NEVER claim "no market on this" without running \`searchAll\` (or at least \`searchPolymarket\`) first. If a search returns zero, say so with the query you tried and offer to broaden.
|
|
348
349
|
|
package/dist/agent/loop.js
CHANGED
|
@@ -939,10 +939,20 @@ export async function interactiveSession(config, getUserInput, onEvent, onAbortR
|
|
|
939
939
|
// never approached its 1M-token compaction threshold. Compact here
|
|
940
940
|
// when the turn has accumulated lots of tool calls AND real spend,
|
|
941
941
|
// even though the context window isn't close to full.
|
|
942
|
+
//
|
|
943
|
+
// Thresholds tightened in 3.15.71. Original 3.15.69 used
|
|
944
|
+
// (>30 calls AND >$0.05) — verified too loose against a real
|
|
945
|
+
// franklin-shorts edit session: 16 deepseek-v4-pro calls for
|
|
946
|
+
// $0.055 ended naturally before the trigger fired, even though
|
|
947
|
+
// by call #4 the per-call input was already 13K tokens (worth
|
|
948
|
+
// compacting). Lowering to (>15 AND >$0.03) catches sessions
|
|
949
|
+
// where input-replay tax has clearly started biting; the
|
|
950
|
+
// fire-once-per-turn flag still bounds the worst case at one
|
|
951
|
+
// extra summary call (~$0.005).
|
|
942
952
|
if (!bloatCompactedThisTurn &&
|
|
943
953
|
compactFailures < 3 &&
|
|
944
|
-
turnToolCalls >
|
|
945
|
-
turnCostUsd > 0.
|
|
954
|
+
turnToolCalls > 15 &&
|
|
955
|
+
turnCostUsd > 0.03) {
|
|
946
956
|
try {
|
|
947
957
|
const beforeTokens = estimateHistoryTokens(history);
|
|
948
958
|
const { history: compacted, compacted: didCompact } = await forceCompact(history, config.model, client, config.debug);
|
package/dist/stats/audit.js
CHANGED
|
@@ -114,7 +114,18 @@ export function extractLastUserPrompt(history) {
|
|
|
114
114
|
const text = flattenContent(msg.content);
|
|
115
115
|
if (!text)
|
|
116
116
|
continue;
|
|
117
|
-
|
|
117
|
+
const cleaned = text.replace(/\s+/g, ' ').trim();
|
|
118
|
+
// Anthropic's message format puts harness-injected context, guardrail
|
|
119
|
+
// warnings, and grounding-retry feedback under role:"user" too. Walking
|
|
120
|
+
// back blindly returns those synthetic strings instead of the real user
|
|
121
|
+
// intent — verified 2026-05-06 in the audit log: 403 entries showed
|
|
122
|
+
// "[FRANKLIN HARNESS PREFETCH] CRCL price..." and 18 showed
|
|
123
|
+
// "[GROUNDING CHECK FAILED] ..." instead of the user's actual question.
|
|
124
|
+
// Skip any message whose first non-whitespace block is a SCREAMING-CASE
|
|
125
|
+
// bracketed label and keep walking back to the real user turn.
|
|
126
|
+
if (/^\[[A-Z][A-Z _-]+\]/.test(cleaned))
|
|
127
|
+
continue;
|
|
128
|
+
return cleaned;
|
|
118
129
|
}
|
|
119
130
|
return undefined;
|
|
120
131
|
}
|
|
@@ -21,14 +21,8 @@
|
|
|
21
21
|
* wallets — P&L, positions, identity
|
|
22
22
|
* smartActivity $0.005 discover markets where high-performing wallets
|
|
23
23
|
* are active right now
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* non-existent path /v1/pm/polymarket/market/<id>/smart-money — that endpoint
|
|
27
|
-
* was never on the gateway, so the action was a silent 404 from day one.
|
|
28
|
-
* Verified 2026-05-05 against blockrun.ai/openapi.json: Polymarket has no
|
|
29
|
-
* per-market path-parameter endpoints; smart-money intelligence lives at
|
|
30
|
-
* /v1/pm/polymarket/markets/smart-activity (cross-market discovery) and
|
|
31
|
-
* /v1/pm/polymarket/leaderboard (top wallets globally).
|
|
24
|
+
* smartMoney $0.005 smart-money positioning on one Polymarket
|
|
25
|
+
* condition_id (per-market drill-down)
|
|
32
26
|
*
|
|
33
27
|
* Output is filtered + truncated on the way back so a single call never
|
|
34
28
|
* dumps 100 markets into the agent's context. Default 20 rows; agents that
|
package/dist/tools/prediction.js
CHANGED
|
@@ -21,14 +21,8 @@
|
|
|
21
21
|
* wallets — P&L, positions, identity
|
|
22
22
|
* smartActivity $0.005 discover markets where high-performing wallets
|
|
23
23
|
* are active right now
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* non-existent path /v1/pm/polymarket/market/<id>/smart-money — that endpoint
|
|
27
|
-
* was never on the gateway, so the action was a silent 404 from day one.
|
|
28
|
-
* Verified 2026-05-05 against blockrun.ai/openapi.json: Polymarket has no
|
|
29
|
-
* per-market path-parameter endpoints; smart-money intelligence lives at
|
|
30
|
-
* /v1/pm/polymarket/markets/smart-activity (cross-market discovery) and
|
|
31
|
-
* /v1/pm/polymarket/leaderboard (top wallets globally).
|
|
24
|
+
* smartMoney $0.005 smart-money positioning on one Polymarket
|
|
25
|
+
* condition_id (per-market drill-down)
|
|
32
26
|
*
|
|
33
27
|
* Output is filtered + truncated on the way back so a single call never
|
|
34
28
|
* dumps 100 markets into the agent's context. Default 20 rows; agents that
|
|
@@ -51,6 +45,7 @@ const PATH_PRICES = [
|
|
|
51
45
|
{ pattern: /\/v1\/pm\/matching-markets/, usd: 0.005 },
|
|
52
46
|
{ pattern: /\/v1\/pm\/polymarket\/wallets\//, usd: 0.005 },
|
|
53
47
|
{ pattern: /\/v1\/pm\/polymarket\/wallet\//, usd: 0.005 },
|
|
48
|
+
{ pattern: /\/v1\/pm\/polymarket\/market\/[^/]+\/smart-money$/, usd: 0.005 },
|
|
54
49
|
{ pattern: /\/v1\/pm\/polymarket\/markets\/smart-activity$/, usd: 0.005 },
|
|
55
50
|
{ pattern: /\/v1\/pm\/.+/, usd: 0.001 },
|
|
56
51
|
];
|
|
@@ -169,21 +164,33 @@ async function extractPaymentReq(response) {
|
|
|
169
164
|
return header;
|
|
170
165
|
}
|
|
171
166
|
// ─── Formatting helpers ────────────────────────────────────────────────────
|
|
167
|
+
function asNumber(value) {
|
|
168
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
169
|
+
return value;
|
|
170
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
171
|
+
const n = Number(value);
|
|
172
|
+
if (Number.isFinite(n))
|
|
173
|
+
return n;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
172
177
|
function formatUsd(value) {
|
|
173
|
-
|
|
178
|
+
const n = asNumber(value);
|
|
179
|
+
if (n == null)
|
|
174
180
|
return 'n/a';
|
|
175
|
-
if (
|
|
176
|
-
return `$${(
|
|
177
|
-
if (
|
|
178
|
-
return `$${(
|
|
179
|
-
if (
|
|
180
|
-
return `$${(
|
|
181
|
-
return `$${
|
|
181
|
+
if (n >= 1e9)
|
|
182
|
+
return `$${(n / 1e9).toFixed(2)}B`;
|
|
183
|
+
if (n >= 1e6)
|
|
184
|
+
return `$${(n / 1e6).toFixed(2)}M`;
|
|
185
|
+
if (n >= 1e3)
|
|
186
|
+
return `$${(n / 1e3).toFixed(1)}K`;
|
|
187
|
+
return `$${n.toFixed(2)}`;
|
|
182
188
|
}
|
|
183
189
|
function formatPct(value, digits = 1) {
|
|
184
|
-
|
|
190
|
+
const n = asNumber(value);
|
|
191
|
+
if (n == null)
|
|
185
192
|
return 'n/a';
|
|
186
|
-
return `${(
|
|
193
|
+
return `${(n * 100).toFixed(digits)}%`;
|
|
187
194
|
}
|
|
188
195
|
// API responses sometimes come wrapped as `{data: [...], pagination: ...}`,
|
|
189
196
|
// other times as a bare array. Normalise to an array.
|
|
@@ -204,11 +211,11 @@ function unwrapList(raw) {
|
|
|
204
211
|
return [];
|
|
205
212
|
}
|
|
206
213
|
async function execute(input, ctx) {
|
|
207
|
-
const { action, search, status, sort, limit, wallets } = input;
|
|
214
|
+
const { action, search, status, sort, limit, conditionId, wallets } = input;
|
|
208
215
|
const cappedLimit = Math.min(Math.max(1, limit ?? DEFAULT_LIMIT), MAX_LIMIT);
|
|
209
216
|
if (!action) {
|
|
210
217
|
return {
|
|
211
|
-
output: 'Error: action is required (searchAll | searchPolymarket | searchKalshi | crossPlatform | leaderboard | walletProfile | smartActivity)',
|
|
218
|
+
output: 'Error: action is required (searchAll | searchPolymarket | searchKalshi | crossPlatform | leaderboard | walletProfile | smartActivity | smartMoney)',
|
|
212
219
|
isError: true,
|
|
213
220
|
};
|
|
214
221
|
}
|
|
@@ -325,10 +332,15 @@ async function execute(input, ctx) {
|
|
|
325
332
|
isError: true,
|
|
326
333
|
};
|
|
327
334
|
}
|
|
328
|
-
// Predexon's batch endpoint
|
|
329
|
-
//
|
|
335
|
+
// Predexon's batch endpoint expects the query param `addresses`,
|
|
336
|
+
// NOT `wallets` — verified 2026-05-06 from a live 422 in a real
|
|
337
|
+
// user session: `{"detail":[{"type":"missing","loc":["query",
|
|
338
|
+
// "addresses"]}]}`. The 3.15.70 ship guessed the param name from
|
|
339
|
+
// the openapi description ("Batch retrieve wallet profiles") and
|
|
340
|
+
// got it wrong. Public field stays `wallets` for ergonomics —
|
|
341
|
+
// we just rename on the wire.
|
|
330
342
|
const raw = await getWithPayment('/v1/pm/polymarket/wallets/profiles', {
|
|
331
|
-
|
|
343
|
+
addresses: wallets.trim(),
|
|
332
344
|
}, ctx);
|
|
333
345
|
const profiles = unwrapList(raw);
|
|
334
346
|
if (profiles.length === 0) {
|
|
@@ -401,6 +413,45 @@ async function execute(input, ctx) {
|
|
|
401
413
|
lines.push('', `_$0.005 paid via x402._`);
|
|
402
414
|
return { output: lines.join('\n') };
|
|
403
415
|
}
|
|
416
|
+
case 'smartMoney': {
|
|
417
|
+
if (!conditionId) {
|
|
418
|
+
return {
|
|
419
|
+
output: 'Error: conditionId is required for smartMoney (Polymarket condition_id from a prior searchPolymarket or smartActivity call)',
|
|
420
|
+
isError: true,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
// Per-market drill-down. Official live registry:
|
|
424
|
+
// /api/v1/pm/polymarket/market/:condition_id/smart-money
|
|
425
|
+
const path = `/v1/pm/polymarket/market/${encodeURIComponent(conditionId)}/smart-money`;
|
|
426
|
+
const data = await getWithPayment(path, {}, ctx);
|
|
427
|
+
const buyers = (data.buyers ?? []).slice(0, 5);
|
|
428
|
+
const sellers = (data.sellers ?? []).slice(0, 5);
|
|
429
|
+
const lines = [
|
|
430
|
+
`## Smart money — \`${conditionId.slice(0, 14)}…\``,
|
|
431
|
+
];
|
|
432
|
+
if (data.net_yes_size != null || data.net_no_size != null) {
|
|
433
|
+
lines.push(`**Net flow:** YES ${formatUsd(data.net_yes_size)} / NO ${formatUsd(data.net_no_size)}`);
|
|
434
|
+
}
|
|
435
|
+
if (buyers.length > 0) {
|
|
436
|
+
lines.push('', '**Top buyers**');
|
|
437
|
+
buyers.forEach((b, i) => {
|
|
438
|
+
const w = b.wallet ? `${b.wallet.slice(0, 8)}…${b.wallet.slice(-4)}` : 'unknown';
|
|
439
|
+
lines.push(`${i + 1}. ${w} — ${formatUsd(b.size)} on ${b.outcome ?? 'unknown side'}`);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
if (sellers.length > 0) {
|
|
443
|
+
lines.push('', '**Top sellers**');
|
|
444
|
+
sellers.forEach((s, i) => {
|
|
445
|
+
const w = s.wallet ? `${s.wallet.slice(0, 8)}…${s.wallet.slice(-4)}` : 'unknown';
|
|
446
|
+
lines.push(`${i + 1}. ${w} — ${formatUsd(s.size)} on ${s.outcome ?? 'unknown side'}`);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (buyers.length === 0 && sellers.length === 0) {
|
|
450
|
+
lines.push('No smart-money flow recorded for this market yet.');
|
|
451
|
+
}
|
|
452
|
+
lines.push('', `_$0.005 paid via x402._`);
|
|
453
|
+
return { output: lines.join('\n') };
|
|
454
|
+
}
|
|
404
455
|
case 'searchPolymarket': {
|
|
405
456
|
const raw = await getWithPayment('/v1/pm/polymarket/markets', {
|
|
406
457
|
search,
|
|
@@ -486,7 +537,7 @@ async function execute(input, ctx) {
|
|
|
486
537
|
}
|
|
487
538
|
default:
|
|
488
539
|
return {
|
|
489
|
-
output: `Error: unknown action "${action}". Use: searchAll, searchPolymarket, searchKalshi, crossPlatform, leaderboard, walletProfile, smartActivity`,
|
|
540
|
+
output: `Error: unknown action "${action}". Use: searchAll, searchPolymarket, searchKalshi, crossPlatform, leaderboard, walletProfile, smartActivity, smartMoney`,
|
|
490
541
|
isError: true,
|
|
491
542
|
};
|
|
492
543
|
}
|
|
@@ -506,12 +557,14 @@ export const predictionMarketCapability = {
|
|
|
506
557
|
'`crossPlatform` (matched market pairs across Polymarket+Kalshi for arbitrage / consensus — $0.005), ' +
|
|
507
558
|
'`leaderboard` (global top wallets by P&L on Polymarket — $0.001), ' +
|
|
508
559
|
'`walletProfile` (P&L + positions for one or more Polymarket wallets — $0.005), ' +
|
|
509
|
-
'`smartActivity` (markets where high-P&L wallets are positioning right now — $0.005)
|
|
560
|
+
'`smartActivity` (markets where high-P&L wallets are positioning right now — $0.005), ' +
|
|
561
|
+
'`smartMoney` (smart-money positioning on one Polymarket condition_id — $0.005). ' +
|
|
510
562
|
'Default routing: ' +
|
|
511
563
|
'"is there a market on X anywhere" → searchAll. ' +
|
|
512
564
|
'"top wallets / who is profitable / who should I follow on Polymarket" → leaderboard. ' +
|
|
513
565
|
'"how is wallet 0xabc doing / show me their P&L" → walletProfile with that address. ' +
|
|
514
566
|
'"what are smart traders betting on right now" → smartActivity. ' +
|
|
567
|
+
'"show smart money on this specific Polymarket market" → smartMoney with conditionId. ' +
|
|
515
568
|
'"should I bet on X" → run searchPolymarket + searchKalshi in parallel and compare implied probabilities — divergence is the signal.',
|
|
516
569
|
input_schema: {
|
|
517
570
|
type: 'object',
|
|
@@ -526,12 +579,13 @@ export const predictionMarketCapability = {
|
|
|
526
579
|
'leaderboard',
|
|
527
580
|
'walletProfile',
|
|
528
581
|
'smartActivity',
|
|
582
|
+
'smartMoney',
|
|
529
583
|
],
|
|
530
584
|
description: 'Which prediction-market query to run. See tool description for cost per action.',
|
|
531
585
|
},
|
|
532
586
|
search: {
|
|
533
587
|
type: 'string',
|
|
534
|
-
description: 'Search query. Used by searchAll / searchPolymarket / searchKalshi / smartActivity. Optional for crossPlatform/leaderboard/walletProfile.',
|
|
588
|
+
description: 'Search query. Used by searchAll / searchPolymarket / searchKalshi / smartActivity. Optional for crossPlatform/leaderboard/walletProfile/smartMoney.',
|
|
535
589
|
},
|
|
536
590
|
status: {
|
|
537
591
|
type: 'string',
|
|
@@ -549,6 +603,10 @@ export const predictionMarketCapability = {
|
|
|
549
603
|
type: 'string',
|
|
550
604
|
description: 'For walletProfile: a single Polymarket wallet address, or a comma-separated list of addresses for batch lookup.',
|
|
551
605
|
},
|
|
606
|
+
conditionId: {
|
|
607
|
+
type: 'string',
|
|
608
|
+
description: 'For smartMoney: Polymarket condition_id from searchPolymarket or smartActivity.',
|
|
609
|
+
},
|
|
552
610
|
},
|
|
553
611
|
required: ['action'],
|
|
554
612
|
},
|
package/package.json
CHANGED