@blockrun/franklin 3.21.8 → 3.22.0

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/README.md CHANGED
@@ -487,6 +487,18 @@ Same wallet. Same tools. From your phone.
487
487
 
488
488
  ---
489
489
 
490
+ ## Contributors
491
+
492
+ External contributors whose work has shipped into Franklin:
493
+
494
+ - [**@KillerQueen-Z**](https://github.com/KillerQueen-Z) — typed Phone & Voice tools (#58), permissions classifier + internal VoiceStatus polling (#59), inline-paste threshold (#60), voicemail controls (#61), PredictionMarket / Predexon v2 schema realignment + agent-loop retry guard (#62)
495
+ - [**@BeneficialVast1048**](https://github.com/BeneficialVast1048) — VoiceCall `interruption_threshold` + `model` controls (#66)
496
+ - [**@TateLyman**](https://github.com/TateLyman) — test path-with-spaces fix (#57)
497
+
498
+ PRs welcome — see the Development section below for the local loop.
499
+
500
+ ---
501
+
490
502
  ## Development
491
503
 
492
504
  ```bash
@@ -305,8 +305,10 @@ On-chain affiliate (20 bps in sell-token, force-set server-side) flows to BlockR
305
305
  - US/CA destinations only. Marketing/sales calls require prior express consent (TCPA).
306
306
 
307
307
  **Surf — crypto data + chat (x402-paid)** via the generic \`BlockRun\` capability. ~55 curated endpoints. Tier-1 $0.001, Tier-2 $0.005, Tier-3 / chat $0.02.
308
- - \`/v1/surf/exchange/*\`CEX trading pairs, prices, perps, depth, klines, funding history, long/short ratio.
309
- - \`/v1/surf/market/*\`token rankings, fear/greed, futures, ETF flows, options skew, liquidations, on-chain indicators (NUPL/SOPR/MVRV), price indicators (RSI/MACD/BBANDS).
308
+ - \`/v1/surf/market/ranking\`token rankings (market cap, volume, change). **This is the token-ranking endpoint — there is no \`market/token-ranking\` or \`market/concept-ranking\`.**
309
+ - \`/v1/surf/market/fear-greed\`Fear & Greed index. \`market/futures\` futures overview. \`market/price\` (needs \`symbol\`) price history. \`market/etf\` (needs \`symbol\`) — spot ETF flows. \`market/options\` (needs \`symbol\`) options skew/IV.
310
+ - \`/v1/surf/market/{liquidation/chart,liquidation/order,onchain-indicator,price-indicator}\` — liquidations, on-chain indicators (NUPL/SOPR/MVRV), technical indicators (RSI/MACD/BBANDS). Tier-2 $0.005.
311
+ - \`/v1/surf/exchange/{price,perp,markets,depth,klines,funding-history}\` — CEX ticker, perps, pairs catalog, depth, klines, funding.
310
312
  - \`/v1/surf/news/{feed,detail}\` — AI-curated crypto news.
311
313
  - \`/v1/surf/onchain/{bridge,yield,gas-price,tx,schema,query,sql}\` — bridge/yield rankings, gas, tx detail, **raw SQL against 80+ indexed chain tables (Tier-3, $0.02)**, structured chain query, schema introspection.
312
314
  - \`/v1/surf/token/{tokenomics,dex-trades,holders,transfers}\` — token analytics.
@@ -315,7 +317,7 @@ On-chain affiliate (20 bps in sell-token, force-set server-side) flows to BlockR
315
317
  - \`/v1/surf/fund/{detail,portfolio,ranking}\` — VC fund profiles, portfolios, ranking.
316
318
  - \`/v1/surf/project/{detail,defi/metrics,defi/ranking}\` — project profiles + DeFi protocol metrics.
317
319
 
318
- For Surf workflows, prefer the bundled skills (\`/surf-market\`, \`/surf-chain\`, \`/surf-social\`) they document which endpoint to pick for which question and the cost trade-off. Skipped (use the dedicated tools instead): \`/v1/surf/prediction-market/*\` (use \`PredictionMarket\`), \`/v1/surf/search/*\` (use \`ExaSearch\`), \`/v1/surf/web/*\` (use \`BrowserX\`). The Surf chat surface (\`/v1/surf/chat/completions\`, surf-1.5) is **not currently exposed** by the BlockRun gateway — removed from the registry pending an upstream redesign around per-token billing. Do not attempt to call it; use the data endpoints above for crypto context, or any of the standard LLMs on \`/v1/chat/completions\` for general chat.
320
+ **Endpoint discovery**: if you are unsure of the exact Surf path, do NOT guess blindly — any wrong \`/v1/surf/...\` path returns a 404 whose body lists every valid endpoint under \`available\`. Read that list and retry with a real path. The bundled skills (\`/surf-market\`, \`/surf-chain\`, \`/surf-social\`) also document which endpoint to pick for which question and the cost trade-off. Skipped (use the dedicated tools instead): \`/v1/surf/prediction-market/*\` (use \`PredictionMarket\`), \`/v1/surf/search/*\` (use \`ExaSearch\`), \`/v1/surf/web/*\` (use \`BrowserX\`). The Surf chat surface (\`/v1/surf/chat/completions\`, surf-1.5) is **not currently exposed** by the BlockRun gateway — removed from the registry pending an upstream redesign around per-token billing. Do not attempt to call it; use the data endpoints above for crypto context, or any of the standard LLMs on \`/v1/chat/completions\` for general chat.
319
321
 
320
322
  **Generic gateway primitive**: \`BlockRun({ path, method, params, body })\` is a single capability that signs x402 and forwards to ANY path under \`/api\`. Use it for Surf endpoints (above) and any future BlockRun partner that doesn't have a dedicated capability yet. Always specify the exact path; the primitive will not guess.
321
323
 
@@ -11,4 +11,10 @@
11
11
  * gateway ever exposes the realized payment amount on the response, that
12
12
  * should be preferred — fall back to this estimate when it's missing.
13
13
  */
14
- export declare function estimateImageCostUsd(model: string, size: string): number;
14
+ /**
15
+ * Estimate the USD cost of `n` images for a model + size. `n` defaults to 1.
16
+ * Unknown models return 0 rather than a guess — a free/custom model should not
17
+ * carry a phantom charge against the Content budget, and surprise overcharging
18
+ * from a wrong guess is worse than under-counting.
19
+ */
20
+ export declare function estimateImageCostUsd(model: string, size: string, n?: number): number;
@@ -11,22 +11,45 @@
11
11
  * gateway ever exposes the realized payment amount on the response, that
12
12
  * should be preferred — fall back to this estimate when it's missing.
13
13
  */
14
- export function estimateImageCostUsd(model, size) {
15
- const m = model.toLowerCase();
14
+ /**
15
+ * Per-image base price by model + size. Mirrors the gateway's IMAGE_MODELS.sizes
16
+ * (blockrun src/lib/models.ts). These are base prices — the realized x402 charge
17
+ * adds a small markup — but they're close enough for budget tracking. Sizes not
18
+ * listed for a model fall back to that model's 1024x1024 tier.
19
+ */
20
+ const PRICE_TABLE = {
21
+ 'openai/dall-e-3': {
22
+ base: 0.04,
23
+ sizes: { '1024x1024': 0.04, '1792x1024': 0.08, '1024x1792': 0.08 },
24
+ },
25
+ 'openai/gpt-image-1': {
26
+ base: 0.02,
27
+ sizes: { '1024x1024': 0.02, '1536x1024': 0.04, '1024x1536': 0.04 },
28
+ },
29
+ 'openai/gpt-image-2': {
30
+ base: 0.06,
31
+ sizes: { '1024x1024': 0.06, '1536x1024': 0.12, '1024x1536': 0.12 },
32
+ },
33
+ 'google/nano-banana': {
34
+ base: 0.05,
35
+ sizes: { '1024x1024': 0.05 },
36
+ },
37
+ 'google/nano-banana-pro': {
38
+ base: 0.1,
39
+ sizes: { '1024x1024': 0.1, '2048x2048': 0.1, '4096x4096': 0.15 },
40
+ },
41
+ };
42
+ /**
43
+ * Estimate the USD cost of `n` images for a model + size. `n` defaults to 1.
44
+ * Unknown models return 0 rather than a guess — a free/custom model should not
45
+ * carry a phantom charge against the Content budget, and surprise overcharging
46
+ * from a wrong guess is worse than under-counting.
47
+ */
48
+ export function estimateImageCostUsd(model, size, n = 1) {
49
+ const entry = PRICE_TABLE[model.toLowerCase()];
50
+ if (!entry)
51
+ return 0;
16
52
  const s = size.replace(/\s+/g, '');
17
- if (m === 'openai/dall-e-3') {
18
- if (s === '1792x1024' || s === '1024x1792')
19
- return 0.08;
20
- // All other sizes fall back to the standard 1024x1024 tier.
21
- return 0.04;
22
- }
23
- if (m === 'openai/gpt-image-1') {
24
- // gpt-image-1 standard tier; larger sizes would tier up but Franklin
25
- // sends 1024x1024 as default.
26
- return 0.042;
27
- }
28
- // Unknown model: return 0 rather than a guess. A free/custom model should
29
- // not have a phantom charge against the Content budget, and surprise
30
- // overcharging from a wrong guess is worse than under-counting.
31
- return 0;
53
+ const perImage = entry.sizes[s] ?? entry.base;
54
+ return perImage * Math.max(1, n);
32
55
  }
@@ -34,7 +34,7 @@ export type RecordImageDecision = {
34
34
  * estimated cost fits; `{ ok: false, reason }` when it doesn't or the
35
35
  * content doesn't exist. Non-mutating.
36
36
  */
37
- export declare function checkImageBudget(library: ContentLibrary, contentId: string, model: string, size: string): {
37
+ export declare function checkImageBudget(library: ContentLibrary, contentId: string, model: string, size: string, count?: number): {
38
38
  ok: true;
39
39
  } | {
40
40
  ok: false;
@@ -20,12 +20,12 @@ import { estimateImageCostUsd } from './image-pricing.js';
20
20
  * estimated cost fits; `{ ok: false, reason }` when it doesn't or the
21
21
  * content doesn't exist. Non-mutating.
22
22
  */
23
- export function checkImageBudget(library, contentId, model, size) {
23
+ export function checkImageBudget(library, contentId, model, size, count = 1) {
24
24
  const content = library.get(contentId);
25
25
  if (!content) {
26
26
  return { ok: false, reason: `Content ${contentId} not found` };
27
27
  }
28
- const cost = estimateImageCostUsd(model, size);
28
+ const cost = estimateImageCostUsd(model, size, count);
29
29
  if (content.spentUsd + cost > content.budgetUsd + 1e-9) {
30
30
  return {
31
31
  ok: false,
@@ -237,12 +237,21 @@ export const blockrunCapability = {
237
237
  }
238
238
  catch { /* best-effort */ }
239
239
  if (!result.ok) {
240
- const detail = typeof result.body?.error === 'string'
241
- ? result.body.error
242
- : `HTTP ${result.status}`;
240
+ const b = result.body;
241
+ const detail = typeof b?.error === 'string' ? b.error : `HTTP ${result.status}`;
243
242
  const fullOutput = result.raw || JSON.stringify(result.body, null, 2);
243
+ // Surface the gateway's self-correction hints into the model-visible
244
+ // output. On a wrong path the Surf route returns `available: [...all
245
+ // valid paths]` (and often a `message`); without this the model only
246
+ // saw "Not Found" and kept guessing until the tool-failure circuit
247
+ // breaker tripped. The list comes straight from the live registry, so
248
+ // it's always complete and in sync — no drift.
249
+ const hint = typeof b?.message === 'string' ? `\n${b.message}` : '';
250
+ const avail = Array.isArray(b?.available)
251
+ ? `\nValid endpoints: ${b.available.filter((x) => typeof x === 'string').join(', ')}`
252
+ : '';
244
253
  return {
245
- output: `BlockRun ${method} ${path} failed: ${detail} (status ${result.status}). No charge if status is 4xx pre-payment.`,
254
+ output: `BlockRun ${method} ${path} failed: ${detail} (status ${result.status}). No charge if status is 4xx pre-payment.${hint}${avail}`,
246
255
  fullOutput,
247
256
  isError: true,
248
257
  };
@@ -5,11 +5,28 @@
5
5
  import type { CapabilityHandler } from '../agent/types.js';
6
6
  import type { ContentLibrary } from '../content/library.js';
7
7
  /**
8
- * Models that accept a reference image via /v1/images/image2image. Currently
9
- * limited to OpenAI's edit endpoint — Gemini Nano Banana Pro and Grok Imagine
10
- * Image Pro need gateway-side support before they can be wired in here.
8
+ * Models that accept a reference image via /v1/images/image2image. Mirrors the
9
+ * gateway's EDIT_SUPPORTED_MODELS (src/app/api/v1/images/image2image/route.ts):
10
+ * both OpenAI gpt-image-* and Google Nano Banana support image-to-image edits.
11
11
  */
12
12
  export declare const EDIT_SUPPORTED_MODELS: Set<string>;
13
+ /**
14
+ * Mask-based inpainting is OpenAI-only. Gemini (Nano Banana) does prompt-based
15
+ * edits with no mask concept. Mirrors the gateway's MASK_SUPPORTED_MODELS.
16
+ */
17
+ export declare const MASK_SUPPORTED_MODELS: Set<string>;
18
+ /**
19
+ * Output-image count ceiling. The gateway has no hard max but price scales with
20
+ * n, so cap client-side to keep a typo from draining the wallet.
21
+ */
22
+ export declare const MAX_OUTPUT_IMAGES = 4;
23
+ /**
24
+ * Valid sizes per known image model, mirroring the gateway's IMAGE_MODELS.sizes
25
+ * (src/lib/models.ts). Used to fail cheaply before paying when a caller or the
26
+ * media router picks a size the model rejects. Models absent from this table
27
+ * (custom / future gateway models) skip validation and let the gateway decide.
28
+ */
29
+ export declare const IMAGE_MODEL_SIZES: Record<string, string[]>;
13
30
  export declare const REFERENCE_IMAGE_MAX_BYTES = 4000000;
14
31
  /**
15
32
  * Normalize a reference image into a base64 data URI for the gateway. The
@@ -24,6 +41,8 @@ export interface ImageGenDeps {
24
41
  /** Invoked after successful content-linked generation; lets callers persist. */
25
42
  onContentChange?: () => void | Promise<void>;
26
43
  }
44
+ /** Insert a `-{idx}` suffix before the file extension: a.png → a-2.png. */
45
+ export declare function withIndexSuffix(p: string, idx: number): string;
27
46
  /**
28
47
  * Build the ImageGen capability. Passing `deps.library` enables the
29
48
  * contentId flow: pre-flight budget check + post-generation asset