From a45ad9a4a6744aaf207a7561aee7ae9a230c8f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:08:41 -0500 Subject: [PATCH 1/6] =?UTF-8?q?feat(stall):=20add=20blockrun=5Fstall=20too?= =?UTF-8?q?l=20=E2=80=94=20209=20pay-per-call=20data=20caps=20via=20The=20?= =?UTF-8?q?Stall=20(IntuiTek=C2=B9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/stall.ts | 112 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/tools/stall.ts diff --git a/src/tools/stall.ts b/src/tools/stall.ts new file mode 100644 index 0000000..d0d72f1 --- /dev/null +++ b/src/tools/stall.ts @@ -0,0 +1,112 @@ +// src/tools/stall.ts +// +// The Stall (IntuiTek¹) — 209 pay-per-call AI data capabilities via x402. +// Path-based GET passthrough so the full cap catalog stays in the stall skill. +// Price range: $0.001–$0.065/call, settled in USDC on Base via x402. +// +// Settlement: each call settles directly to The Stall's Base treasury. +// BlockRun routes the request server-side to https://the-stall.intuitek.ai + +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { checkBudget, recordSpending } from "../utils/budget.js"; +import { getClient } from "../utils/wallet.js"; +import { formatError, extractErrorMessage } from "../utils/errors.js"; +import type { BudgetState } from "../types.js"; + +type StallClient = { + getWithPaymentRaw: (endpoint: string, params?: Record) => Promise; +}; + +// Cost estimates for local budget pre-check. +// Actual settlement is upstream-authoritative (from the 402 response body). +const STALL_HIGH_CAPS = new Set([ + "company-due-diligence", "strategy-signal", "portfolio-risk-check", + "defi-yield-scan", "regulatory-risk-scan", "patent-search", "sanctions-check", +]); + +const STALL_MID_CAPS = new Set([ + "us-stock-price", "us-stock-history", "us-stock-news", "us-stock-financials", + "eu-stock-price", "eu-stock-history", "jp-stock-price", "jp-stock-history", + "kr-stock-price", "kr-stock-history", "au-stock-price", "au-stock-history", + "market-gex", "options-chain", "options-flow", "insider-trades", + "congressional-trades", "sec-edgar-search", "sec-edgar-filing", + "twitter-intel", "reddit-intel", "polymarket-intel", +]); + +function estimateStallCost(cap: string): number { + const c = cap.toLowerCase(); + if (STALL_HIGH_CAPS.has(c)) return 0.065; + if (STALL_MID_CAPS.has(c)) return 0.015; + if ( + c.startsWith("us-stock") || c.startsWith("eu-stock") || c.startsWith("jp-stock") || + c.startsWith("kr-stock") || c.startsWith("au-stock") || c.startsWith("options-") || + c.startsWith("insider-") || c.startsWith("sec-edgar") + ) return 0.015; + return 0.003; +} + +export function registerStallTool(server: McpServer, budget: BudgetState): void { + server.registerTool( + "blockrun_stall", + { + description: `209 pay-per-call AI data capabilities via The Stall (IntuiTek¹). No API keys. USDC on Base. + +Coverage (full catalog and worked examples in the stall skill): +- Equities: US/EU/JP/KR/AU stock prices, OHLCV, earnings, dividends, financials, analyst estimates +- Options microstructure: dealer GEX (gamma exposure), gamma flip level, vol regime via CBOE data +- Crypto & DeFi: token prices, DEX liquidity, Polymarket intelligence, DeFiLlama TVL, yield pools +- Macro: Treasury yields, FRED economic series, G10 FX rates, World Bank GDP/inflation +- Regulatory: congressional trades, insider filings, SEC/EDGAR search, sanctions screening +- News & signals: GDELT 24h global synthesis, Reddit social momentum, Hacker News intelligence +- Climate & weather: 85-year ERA5 historical climate, live weather, air quality index +- Social: Twitter/X user lookup and tweet search (x402-to-x402 via twit.sh) +- Specialized: patent search, Wayback Machine archive access, meme token radar (Solana) + +Pricing: $0.001–$0.065/call. Full 209-cap list: https://the-stall.intuitek.ai/health + +Selected caps (full catalog in the stall skill): + us-stock-price ticker=AAPL ($0.018) + market-gex ticker=SPY days_out=21 ($0.020) + polymarket-intel query=bitcoin ($0.008) + global-news-intel topic=federal+reserve ($0.003) + treasury-rates maturity=10Y ($0.001) + climate-history lat=40.7 lon=-74.0 years=30 ($0.005) + sec-edgar-search q=apple+buyback ($0.007) + insider-trades ticker=NVDA ($0.008) + twitter-intel action=lookup_user handle=sama ($0.015) + defi-tvl protocol=uniswap ($0.003)`, + inputSchema: { + cap: z.string().describe("Capability name, e.g. 'us-stock-price', 'market-gex', 'polymarket-intel'. Full list: https://the-stall.intuitek.ai/health"), + params: z.record(z.string(), z.string()).optional().describe("Query parameters for the cap, e.g. { ticker: 'AAPL' } or { query: 'bitcoin', limit: '5' }"), + agent_id: z.string().optional().describe("Agent identifier for budget tracking and enforcement."), + }, + }, + async ({ cap, params, agent_id }) => { + try { + const cleanCap = cap.replace(/^\/+/, "").replace(/\.js$/, "").toLowerCase(); + const estimatedCost = estimateStallCost(cleanCap); + const budgetCheck = checkBudget(budget, agent_id, estimatedCost); + if (!budgetCheck.allowed) { + return { + content: [{ type: "text", text: `${budgetCheck.reason}. Use blockrun_wallet action:"report" to see usage or action:"delegate" to increase agent budget.` }], + isError: true, + }; + } + + const client = getClient() as unknown as StallClient; + const result = await client.getWithPaymentRaw(`/v1/stall/${cleanCap}`, params); + recordSpending(budget, estimatedCost, agent_id); + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + structuredContent: result as Record, + }; + } catch (err) { + return { + content: [{ type: "text", text: formatError(extractErrorMessage(err)) }], + isError: true, + }; + } + } + ); +} From 5280d52cf7a8d83f31ddddd2fa3b808049927d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:08:47 -0500 Subject: [PATCH 2/6] =?UTF-8?q?feat(stall):=20add=20stall=20skill=20?= =?UTF-8?q?=E2=80=94=20209-cap=20quick=20decision=20table=20+=20worked=20e?= =?UTF-8?q?xamples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/stall/SKILL.md | 223 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 skills/stall/SKILL.md diff --git a/skills/stall/SKILL.md b/skills/stall/SKILL.md new file mode 100644 index 0000000..391dacb --- /dev/null +++ b/skills/stall/SKILL.md @@ -0,0 +1,223 @@ +--- +name: stall +description: Use when an agent needs financial data, market intelligence, or economic data that isn't available from blockrun_markets, blockrun_price, or blockrun_surf. The Stall covers 209 pay-per-call capabilities: US/EU/JP/KR/AU equities, dealer gamma exposure (GEX), DeFi/Polymarket, GDELT global news synthesis, SEC/EDGAR filings, congressional trades, insider activity, 85-year ERA5 climate history, Twitter/X intelligence, Reddit social momentum, and more. USDC on Base; no API keys. +triggers: + - "stock price" + - "stock history" + - "earnings" + - "dividends" + - "analyst estimates" + - "japanese stock" + - "korean stock" + - "australian stock" + - "european stock" + - "eu stock" + - "dealer gamma" + - "gamma exposure" + - "GEX" + - "gamma flip" + - "vol regime" + - "options microstructure" + - "congressional trades" + - "insider trades" + - "SEC filing" + - "EDGAR" + - "regulatory filing" + - "sanctions" + - "GDELT" + - "global news" + - "reddit momentum" + - "hacker news" + - "social momentum" + - "climate history" + - "weather history" + - "ERA5" + - "air quality" + - "treasury rates" + - "yield curve" + - "FRED data" + - "World Bank" + - "patent search" + - "wayback machine" + - "internet archive" + - "meme coin" + - "solana dex" + - "defi tvl" + - "defillama" + - "polymarket" + - "prediction market" + - "twitter intel" + - "tweet search" + - "X intelligence" + - "company due diligence" + - "sanctions check" +--- + +# The Stall — 209 Pay-Per-Call AI Data Capabilities + +The Stall is an x402-native data server built by IntuiTek¹. 209 capabilities, no API keys, USDC on Base. BlockRun routes to `https://the-stall.intuitek.ai` and settles directly to The Stall's Base treasury. + +Complementary to `blockrun_surf` (on-chain/crypto SQL) and `blockrun_markets` (prediction market aggregation) — The Stall covers equities, macro, regulatory, climate, social, and specialized research that neither of those tools reaches. + +## How to Call from MCP + +One tool, two params. All calls are GET with query params: + +```ts +blockrun_stall({ cap: "us-stock-price", params: { ticker: "AAPL" } }) +blockrun_stall({ cap: "market-gex", params: { ticker: "SPY", days_out: "21" } }) +blockrun_stall({ cap: "global-news-intel", params: { topic: "federal reserve" } }) +``` + +Full cap list: `curl https://the-stall.intuitek.ai/health | jq '.capabilities'` + +## Pricing Tiers + +| Tier | Price | Used by | +|------|-------|---------| +| Low | $0.001–$0.005 | Macro reads (Treasury, FRED, FX), news synthesis, weather, DeFi TVL, social signals | +| Mid | $0.007–$0.020 | Equities (all markets), GEX, options data, SEC filings, insider/congressional trades, social intel | +| High | $0.030–$0.065 | Synthesis caps (due diligence, strategy signal, portfolio risk, patent search) | + +All prices from the live 402 response — `estimateStallCost` is a local budget guard only. + +## Quick Decision Table + +| User wants… | Cap | Required params | Price | +|---|---|---|---| +| US stock current price | `us-stock-price` | `ticker` | $0.018 | +| US stock OHLCV history | `us-stock-history` | `ticker`, `period` (1d/5d/1mo/3mo/1y) | $0.012 | +| US stock earnings/financials | `us-stock-financials` | `ticker` | $0.015 | +| US stock recent news | `us-stock-news` | `ticker` | $0.012 | +| EU stock price | `eu-stock-price` | `ticker` (e.g. ASML.AS) | $0.015 | +| Japan stock price | `jp-stock-price` | `ticker` (e.g. 7203.T) | $0.015 | +| Korea stock price | `kr-stock-price` | `ticker` (e.g. 005930.KS) | $0.015 | +| Australia stock price | `au-stock-price` | `ticker` (e.g. BHP.AX) | $0.015 | +| Dealer GEX for any ticker | `market-gex` | `ticker`, `days_out` (opt, default 45) | $0.020 | +| Options chain | `options-chain` | `ticker`, `expiry` (opt) | $0.018 | +| Congressional stock trades | `congressional-trades` | `ticker` (opt), `days` (opt, default 30) | $0.008 | +| Insider filings | `insider-trades` | `ticker` | $0.008 | +| SEC/EDGAR filings search | `sec-edgar-search` | `q` (keyword) | $0.007 | +| Specific SEC filing | `sec-edgar-filing` | `ticker`, `form` (10-K/10-Q/8-K) | $0.010 | +| Sanctions/watchlist check | `sanctions-check` | `name` or `address` | $0.008 | +| GDELT global news synthesis | `global-news-intel` | `topic` | $0.003 | +| Reddit social momentum | `reddit-intel` | `ticker` or `topic` | $0.010 | +| Hacker News intelligence | `hackernews-intel` | `topic` | $0.005 | +| Twitter/X user profile | `twitter-intel` | `action=lookup_user`, `handle` | $0.015 | +| Twitter/X tweet search | `twitter-intel` | `action=search_tweets`, `query` | $0.015 | +| Live US Treasury yields | `treasury-rates` | `maturity` (1M/3M/6M/1Y/2Y/5Y/10Y/30Y) | $0.001 | +| Full yield curve | `yield-curve` | – | $0.002 | +| FRED economic series | `fred-series` | `series_id` (e.g. UNRATE, CPI, GDP) | $0.002 | +| G10 FX rates | `forex-rates` | `base` (USD/EUR/etc), `pairs` (opt) | $0.002 | +| World Bank GDP/inflation | `world-bank-data` | `country`, `indicator` | $0.003 | +| DeFi protocol TVL | `defi-tvl` | `protocol` | $0.003 | +| DeFi yield pools | `defi-yields` | `chain` (opt), `min_apy` (opt) | $0.003 | +| Polymarket prediction | `polymarket-intel` | `query` | $0.008 | +| 85-year climate history | `climate-history` | `lat`, `lon`, `years` (opt, default 30) | $0.005 | +| Live weather | `weather` | `lat`, `lon` or `city` | $0.002 | +| Air quality index | `air-quality` | `lat`, `lon` or `city` | $0.002 | +| Patent search | `patent-search` | `q`, `limit` (opt) | $0.040 | +| Wayback Machine archive | `wayback-intel` | `url`, `year` (opt) | $0.003 | +| Solana meme token radar | `meme-radar` | `limit` (opt, default 10) | $0.005 | +| Company due diligence | `company-due-diligence` | `company` or `ticker` | $0.060 | +| Technical strategy signal | `strategy-signal` | `ticker` | $0.065 | + +## Worked Examples + +### 1. "What's Apple's stock price and how has it moved this week?" + +```ts +// Current price +blockrun_stall({ cap: "us-stock-price", params: { ticker: "AAPL" } }) +// 5-day history +blockrun_stall({ cap: "us-stock-history", params: { ticker: "AAPL", period: "5d" } }) +``` +**Cost: $0.018 + $0.012 = $0.030.** Returns price, volume, market cap, 52-week range. + +### 2. "Is SPY pinned? What's the gamma flip level?" + +```ts +blockrun_stall({ cap: "market-gex", params: { ticker: "SPY", days_out: "21" } }) +``` +**Cost: $0.020.** Returns `total_gex` (positive = dealers are long gamma / pinning effect), `gamma_flip` (the strike where net GEX crosses zero), `vol_regime` (pinned/neutral/acceleration), and the top GEX-contributing strikes. Methodology: standard SpotGamma-style dealer gamma calculation from CBOE delayed options data — same approach as paid services at $1,200/year. + +### 3. "Have any senators or reps traded NVDA recently?" + +```ts +blockrun_stall({ cap: "congressional-trades", params: { ticker: "NVDA", days: "60" } }) +``` +**Cost: $0.008.** Returns trades from the STOCK Act filings: politician name, party, trade date, action (buy/sell), estimated amount range, filing date. + +### 4. "What's the global news on the Federal Reserve this week?" + +```ts +blockrun_stall({ cap: "global-news-intel", params: { topic: "Federal Reserve interest rates" } }) +``` +**Cost: $0.003.** GDELT indexes 100+ languages across 215 countries. Returns a synthesized summary of 24h coverage with source count, sentiment, geographic spread, and top article links. + +### 5. "Run a full macro snapshot: yields + FX + FRED unemployment" + +```ts +// 10Y Treasury +blockrun_stall({ cap: "treasury-rates", params: { maturity: "10Y" } }) +// Full yield curve +blockrun_stall({ cap: "yield-curve" }) +// EUR/USD +blockrun_stall({ cap: "forex-rates", params: { base: "USD" } }) +// FRED unemployment rate +blockrun_stall({ cap: "fred-series", params: { series_id: "UNRATE" } }) +``` +**Cost: 4 × ~$0.002 = $0.008.** All free-tier macro reads. + +### 6. "What was the climate like in NYC 30 years ago vs. today?" + +```ts +blockrun_stall({ cap: "climate-history", params: { lat: "40.7128", lon: "-74.0060", years: "30" } }) +``` +**Cost: $0.005.** ERA5 reanalysis data. Returns 30-year monthly averages for temperature, precipitation, humidity vs. the most recent year — useful for long-horizon climate trend analysis. + +### 7. "Is this wallet sanctioned? Check OFAC, UN, EU lists." + +```ts +blockrun_stall({ cap: "sanctions-check", params: { address: "0xd882cfc20f52f2599d84b8e8d58c7fb62cfe344b" } }) +``` +**Cost: $0.008.** Checks OFAC SDN, UN Security Council, EU Consolidated, UK HMT, and FATF grey/black lists. Returns `sanctioned: true/false`, matched lists, and entity name if found. + +### 8. "What's trending on Reddit about Tesla?" + +```ts +blockrun_stall({ cap: "reddit-intel", params: { ticker: "TSLA" } }) +``` +**Cost: $0.010.** Returns post volume trend (7d), sentiment score, top posts with engagement, subreddit breakdown (r/wallstreetbets, r/investing, r/stocks, etc.). + +### 9. "Full due diligence on Anthropic" + +```ts +blockrun_stall({ cap: "company-due-diligence", params: { company: "Anthropic" } }) +``` +**Cost: $0.060.** Multi-source synthesis: SEC/EDGAR if public, news coverage (GDELT), web intelligence, recent filings, leadership, funding history, competitive position. Returns a structured report. + +### 10. "What's the Wayback Machine snapshot of OpenAI's homepage from 2020?" + +```ts +blockrun_stall({ cap: "wayback-intel", params: { url: "https://openai.com", year: "2020" } }) +``` +**Cost: $0.003.** Returns the closest archived snapshot URL + extracted text content from the Internet Archive. + +## Gotchas + +- **All caps are GET with query params** — unlike blockrun_surf/blockrun_exa, there are no POST endpoints. Pass everything in `params`. +- **EU/JP/KR/AU stock tickers require the exchange suffix**: ASML.AS (Amsterdam), 7203.T (Tokyo), 005930.KS (Korea), BHP.AX (ASX). US tickers are bare (AAPL, not AAPL.US). +- **`market-gex` uses CBOE delayed data** (15-min delay for US equities). Suitable for positioning/regime analysis, not for intraday HFT. +- **`twitter-intel` requires `action` param**: `action=lookup_user` (profile) or `action=search_tweets` (keyword search). Settles upstream via twit.sh x402 relay. +- **`climate-history` uses ERA5 reanalysis** — gridded model output, not station observations. Accuracy ±0.1°C monthly average. +- **`patent-search` returns US patents via USPTO**. International patents not included. +- **Missing required param → 400 without charge** — The Stall validates before settling. + +## Reference + +- Full health + cap list: https://the-stall.intuitek.ai/health +- Well-known x402 pricing: https://the-stall.intuitek.ai/.well-known/x402 +- MCP endpoint (free): https://the-stall.intuitek.ai/mcp +- Publisher: IntuiTek¹ — kyle@intuitek.ai +- Settlement wallet (Base): 0x03d773c52B67993e60Ecb3134b17436fE03B584c From 1d6dba2bc931d31dbb6590a8ecc57c74be686f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:10:25 -0500 Subject: [PATCH 3/6] feat(stall): register blockrun_stall tool in mcp-handler --- src/mcp-handler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mcp-handler.ts b/src/mcp-handler.ts index 8545a6b..4410c13 100644 --- a/src/mcp-handler.ts +++ b/src/mcp-handler.ts @@ -22,6 +22,7 @@ import { registerPhoneTool } from "./tools/phone.js"; import { registerSurfTool } from "./tools/surf.js"; import { registerRpcTool } from "./tools/rpc.js"; import { registerDefiTool } from "./tools/defi.js"; +import { registerStallTool } from "./tools/stall.js"; export function initializeMcpServer(server: McpServer): void { const budget: BudgetState = { limit: null, spent: 0, calls: 0, agents: new Map() }; const modelCache: ModelCache = { models: null }; @@ -45,6 +46,7 @@ export function initializeMcpServer(server: McpServer): void { registerSurfTool(server, budget); registerRpcTool(server, budget); registerDefiTool(server, budget); + registerStallTool(server, budget); // Register resources server.registerResource( From fca188b3952b69587cf60c3eaf22d2463db4e7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:10:31 -0500 Subject: [PATCH 4/6] chore: bump version to 0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 52153c1..46361ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blockrun/mcp", - "version": "0.21.5", + "version": "0.22.0", "mcpName": "io.github.BlockRunAI/blockrun-mcp", "description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, crypto data, X/Twitter intelligence. Paid via x402 micropayments.", "type": "module", From 499444551fbcb9524bcd1e16bc01e2a7ae5b56bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:10:36 -0500 Subject: [PATCH 5/6] docs(stall): add blockrun_stall to Tools table in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 522ed42..b74f7aa 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ $5 covers ~5,000 market queries, ~500 Exa searches, ~250 image generations, or ~ | `blockrun_dex` | Live DEX prices via DexScreener | free | | `blockrun_rpc` | Raw JSON-RPC on 40+ chains (Ethereum, Base, Solana, Bitcoin, Sui, NEAR, ...) via Tatum gateway — eth_call, balances, blocks, logs | $0.002/call | | `blockrun_defi` | DefiLlama — protocol TVL, chain TVL, yield pools (APY), token prices | $0.001–0.005/call | +| `blockrun_stall` | The Stall (IntuiTek¹) — 209 data caps: US/EU/JP/KR/AU equities, dealer GEX, DeFi, Polymarket, GDELT news, SEC/EDGAR, congressional trades, 85yr ERA5 climate, social intelligence | $0.001–$0.065/call | | `blockrun_modal` | Isolated code execution in a BlockRun-hosted Modal sandbox — disposable container, optional GPU (T4 → H100) | $0.01 create; $0.001/op | | `blockrun_phone` | Outbound AI voice calls (Bland) + wallet-owned US/CA numbers (Twilio), carrier + fraud lookups | $0.54/call; $5/number | | `blockrun_models` | Live catalogue of every LLM/image/video/music model + pricing | free | From 5590111ee0fd1ee02a16289f83ae76afcf4e5162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C2=B9yle=20Million?= Date: Wed, 10 Jun 2026 21:10:42 -0500 Subject: [PATCH 6/6] docs(stall): add 0.22.0 CHANGELOG entry for blockrun_stall --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6668326..c1b449e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to BlockRun MCP will be documented in this file. +## 0.22.0 + +- **`blockrun_stall` — 209 pay-per-call AI data capabilities via The Stall (IntuiTek¹).** Equities (US/EU/JP/KR/AU stocks, options microstructure, dealer GEX via CBOE), Crypto/DeFi (Polymarket, DeFiLlama, DEX data), Macro (FRED, Treasury, FX, World Bank), Regulatory (SEC/EDGAR, congressional trades, insider filings, sanctions), News (GDELT 24h synthesis), Climate (85yr ERA5 history), Social (Twitter/X via twit.sh, Reddit, HN), and more. $0.001–$0.065/call, USDC on Base. + - Full 209-cap catalog: https://the-stall.intuitek.ai/health + - Thanks @KyleMillion (IntuiTek¹) + ## 0.21.5 - **`refactor` — internal cleanup, no behavior change.** Collapsed four byte-identical `fetchWithTimeout` copies (music, speech, video, realface) and the duplicated model-cache loader into shared `utils/http.ts` and `utils/model-cache.ts`. Both the abort timer and the model-cache TTL timer are now `.unref()`'d, so a pending request or expiring cache never keeps the stdio process alive. Added `isTimeoutError()` (checks the `AbortError`/`TimeoutError` DOMException name before falling back to message substrings) and used it for the music/speech/video timeout branches, replacing the previous inconsistent per-tool string matching.