AI 资讯
Why I Built OpenAgentFlow: Decoupling Multi-Agent Workflows from Framework Boilerplate
Hey everyone, my name is AbdulRahman Elzahaby ( @egyjs ), a software engineer from Egypt who’s recently fallen down the rabbit hole of LLMs. Like so many of us, you’ll find me in the thick of automation, bots, and making AI work for fast-tracking features and workflows. As I started diving into complex, multi-agent workflow automation, I experimented with… everything. I fiddled with n8n, played with OpenClaw, tinkered with Hermes Agent, andspent what felt like ages manually chaining together Python scripts with LangChain and LangGraph. Every tool showed promise, but my work became increasingly complex and every single workflow somehow felt... Unfinished. The way the industry seems to approach building these kinds of agents revealed a massive, structural gap in tool design. On one hand, we have intuitive, visual workflow tools like n8n. The drag-and-drop interface is great for a bird’s eye view of higher-level logic. But when you need more advanced concepts, like complex looping with conditional logic, custom state reduction, or a system that integrates properly with code review and versioning, these low-code boxes quickly hit limitations. On the other hand, we have powerful code-first frameworks like LangGraph. They provide incredible flexibility and raw execution power, but as soon as you start to build even a simple three-agent triage workflow, the boilerplate code starts to pile up. You have to write custom state schemas (TypedDict), initialize every node function individually, define custom logic for how to route between agents, set up the graph checkpointer, and grapple with environment and dependency management. What seemed missing was a sweet spot - a seamless bridge between the design intuition of visual workflows and the production-ready execution power of code-based frameworks. I wanted a tool that allowed me to simply design a workflow, and then run it efficiently without getting tangled in repetitive boilerplate code. Ultimately, I concluded that what we
创业投融资
Meet the judges who will crown Australia’s next breakout startup
TechCrunch Startup Battlefield is coming to Australia — and we're partnering with Stripe to find the country's most exciting early-stage startups.
AI 资讯
MIT Hackathon Puzzle That Turned Into a Data Science Project
How a face-customization puzzle at HackMIT went from clicking sliders by hand to reverse-engineering a hidden formula from 10,000 API calls. Face Value looked simple at first glance: ten sliders (Face, Skin, Hair, Brows, Eyes, Nose, Mouth, Glasses, Mole, Accessory), each 0-9, controlling a cartoon avatar. A hidden model scored every configuration, and the goal was to find one it would fully accept : Confidence ≥ 99.9% Edit distance from the starter config ≤ 5 (only half the sliders could move) Charm check: pass Sync check: pass The puzzle's own hint: "Not all features affect the model equally. Some are more sensitive than others, especially together. Single-feature sweeps can be misleading." That warning turned out to be the whole game. Phase 1: Brute Force by Hand The first instinct is the obvious one: click a slider, hit Query, read the result, adjust, repeat. Every query returned four numbers, shown together in a Reviewer panel: Probability, Charm, edit Distance, and Sync. All four had to align at once. This works, sort of. Over the first ~24 manual queries, real patterns emerged: certain Glasses values seemed to matter for Sync, Mole and Accessory nudged confidence up, some sliders had sharp peaks rather than smooth slopes. But progress plateaued hard around 60-77% confidence . Manual testing can only really explore one or two dimensions at a time, and the puzzle explicitly warned that the model cared about combinations ; you can't discover a 3-way interaction by changing one slider and squinting at the result. The first real breakthrough was small but important: after enough fiddling, one query came back with Sync: True for the first time, confidence still low (8.14%), but proof that the four conditions weren't mutually exclusive. Phase 2: Escaping the UI The turning point was popping open Chrome DevTools, clicking Query once, and grabbing the actual network request as a curl command. Underneath the slick UI was a plain JSON API: POST https://facevalue.hackmit.
AI 资讯
The rules were written down. Nobody followed them. Then CI went red on day one
My project's docs had rules. "One document, one responsibility." "Split anything over 45 lines." I wrote that. The day before yesterday. Here's how that was going. Folders with no README (no index): 25 out of 37 The folder holding our engineering rules: 11 files, zero index Documents breaking the 45-line rule: 47 Largest offender: 1,203 lines Writing a rule down does not make it a rule. Obvious, I know. But there is something about being handed a list of 47 documents where you personally broke your own rule that stops being funny halfway down. (The me of two days ago fully intended to follow it.) I rewrote the rules themselves, and the rules file hit 150 lines The plan was already clear: let a machine enforce this. Fail CI. Which meant writing the rules properly first. README required, folder layout, update obligations, what CI actually checks. By the time it was all in there, the rules file was over 150 lines. The rules file was breaking the 45-line rule. Now, if you say "well, the rules file is special, it gets an exemption" — what have you just done? You have created the precedent "the rules are exempt," and it is permanent from that day. From then on, every time someone crosses 45 lines, they get to say "the rules file does it too." And they're right. So I split it. Eight files, all under 45 lines. If I can't follow my own rule, the rule was never worth writing. The moment CI landed, 63 existing violations bared their teeth On to the real work: write the checker, wire it into CI. Run it, and of course: 63 violations Everything fails. All red. Files I'm about to touch and files nobody has opened in months, equally red. Humanity is offered two choices here. Fix all 63 first, then turn on CI (including the 1,203-line monster) Add an ignore list, silence the 63, move on Tempting, isn't it? Option 2. It was to me. A .lintignore with 63 lines in it and a comment saying "remove later." So when exactly are you removing that list? Think about what that file actually is.
AI 资讯
Privacy-First Health: Running Llama-3 Locally on iPhone with MLX-Swift
In the age of "Cloud Everything," our most sensitive data—our heartbeat, our sleep cycles, our stress levels—often ends up on a server somewhere in Northern Virginia. But what if we could keep that data where it belongs? On your device. Today, we're diving deep into Edge AI and On-device LLMs . We will build a privacy-centric health coach that uses MLX-Swift to run Llama-3 directly on your iPhone's Apple Silicon. We’ll be pulling real-time Heart Rate Variability (HRV) data from the HealthKit API and generating semantic health summaries without a single byte ever leaving your phone. 🚀 Why Edge AI? 🛡️ When dealing with Private AI and sensitive medical metrics, the "Cloud-First" approach is a liability. By leveraging MLX-Swift and the Unified Memory Architecture of the A17 Pro/A18 chips, we achieve: Zero Latency : No round-trip to a server. Total Privacy : Your data stays in the Secure Enclave. Offline Capability : Health insights in the middle of the woods? Yes. The Architecture 🏗️ The data flow is simple but powerful. We fetch raw samples from HealthKit, preprocess them into a prompt-friendly format, and feed them into a quantized Llama-3 model managed by the MLX framework. graph TD A[iPhone HealthKit Store] -->|Fetch HRV Samples| B(Swift Data Controller) B -->|Normalize & Format| C{MLX-Swift Engine} D[Llama-3-8B-4bit Model] -->|Load Weights| C C -->|Local Inference| E[Neural Engine / GPU] E -->|Semantic Summary| F[SwiftUI Dashboard] F -->|User Feedback| A Prerequisites 🛠️ To follow this advanced tutorial, you'll need: Xcode 15.4+ and a physical iPhone (iPhone 15 Pro or newer recommended for 8GB+ RAM). MLX-Swift : Apple's framework for machine learning on Apple Silicon. Llama-3-8B (4-bit quantized) : To fit within the iOS memory footprint. HealthKit Permissions : Configured in your Info.plist . Step 1: Accessing HealthKit Data 💓 First, we need to grab that juicy HRV data. Heart Rate Variability is a key indicator of autonomic nervous system stress. import HealthKit c
AI 资讯
OhNine: Why I Built a Menu Bar App for Claude Limits
OhNine is a free menu bar app that tracks Claude session and weekly usage limits in real time It sends native alerts at 80%, 91%, and 100% so a session never ends without warning The hard problem was never reading a number, it was making the warning arrive before the cutoff instead of after Building a zero telemetry tool changed how I judge every product I ship after it The Problem: Hitting a Wall You Cannot See For months, my Claude sessions ended the same frustrating way. I would be deep in a conversation, mid thought, actually making progress, and then the reply would just stop. No countdown. No yellow light. No warning that said "you have three messages left, wrap up." One second I was working, the next I was staring at a message telling me to wait for a reset I never saw coming. The frustrating part was not the limit itself. Usage limits exist for a reason, and I understand why they are there. The frustrating part was the total lack of visibility into where I stood. Claude Code and claude.ai will occasionally mention you are close to a cap, sometimes at 97 percent, which is technically a warning and practically useless, because by then you are already mid-thought with no time left to land it cleanly. It got worse once I noticed the layers. There is not one limit to track, there are several stacked on top of each other: a session limit, a rolling weekly cap, and separate caps depending on which model you are running. Switching models mid-session, thinking you had found a workaround, only to hit a wall from a different direction, was its own specific kind of frustrating. None of these layers showed up anywhere. There was no dashboard, no menu bar icon, nothing you could glance at the way you glance at your laptop's battery percentage before deciding whether to plug in. So the wall kept arriving the same way: mid-flow, mid-sentence, with zero warning. Coding sessions got cut off between a question and its answer. Writing sessions lost momentum at the worst possibl
AI 资讯
Knowledge-and-Memory-Management v0.0.2: Portable Knowledge Collection and Memory Management
Welcome to the v0.0.2 release of Knowledge-and-Memory-Management, a tool designed for ingesting and managing knowledge from diverse sources. This release marks a clean release, stripping all hardcoded personal paths and replacing them with the portable $AGENT_HOME environment variable. For experienced developers, this version brings consistency and ease of deployment across environments without sacrificing the core functionality of knowledge collection and memory management. The project focuses on three primary collection pipelines: web, video, and articles. Each pipeline is modular, allowing you to configure, extend, or replace components based on your stack. The memory management layer ensures that collected data is indexed, stored, and retrievable via semantic search, making it practical for building personal knowledge bases or feeding into larger systems like agents or RAG pipelines. Knowledge Collection The collection module is source-agnostic at its core but ships with specialized handlers for common content types. Web collection uses a configurable web scraper that supports depth limits, domain filtering, and content extraction via readability algorithms. You can target specific sections, strip ads, and normalize HTML into markdown. The scraper respects robots.txt and supports session management for authenticated sites. Video collection transcribes audio using a local or remote ASR model. The pipeline extracts audio tracks, splits them into chunks, and generates timestamped transcripts. This is particularly useful for processing lectures, talks, or screencasts. The transcript is treated as a text document for further processing. Article collection handles RSS/Atom feeds and direct URLs. It parses feeds, fetches full content using readability engines, and deduplicates entries. Articles are converted into a consistent schema: title, author, published date, body text, and metadata. All collected data passes through a normalizer that converts content into a stand
AI 资讯
The AI Can't See What It Drew
Originally published on hexisteme notes . A while back I wrote about why your vibe-coded app looks worse than you expect. That post diagnosed the cause. This one is the fix that actually worked, on a real job: redesigning the mascot in my trip expense-splitting app. The mascot is the face of the app. It shows up in more than twenty places — onboarding, settings, the stats screen, the map, the diary, the settlement report, and five little mini-games. And it was nothing. One circle did double duty as head and body. No legs. No hands. No eyebrows. One X for an eye. Visually its identity was zero: a tinted circle. I knew it was bad. What I could not do was say what to change. Words don't converge on a picture I kept talking myself in circles about it, and so did the AI I was pairing with. Rounder? Add a hat? Bigger eyes? Every sentence sounded reasonable and none of them moved the decision. At some point I noticed what was actually going on: this was not a shortage of information. Nobody needed to go fetch a fact. It was a shortage of fidelity . A visual decision cannot converge in prose, because prose is not the medium the decision lives in. That is the tell. When a discussion loops and more words don't help, you don't need more analysis — you need a picture. So I stopped arguing and built prototypes. Three variants, not more tints The rule I gave myself: make variants that are structurally different, not palette swaps. Different silhouette, different anatomy, a different device carrying the identity. Repainting the same shape in different colors teaches you nothing. Three genuinely different creatures force a real choice. I built three and rendered every one as an action sheet so I could look at them side by side: A, a jelly bean. The safe evolution of what I already had. It slots into the UI cleanly, but its whole identity hangs on a single coin floating over its head. Shrink it and it's just a round blob again. B, a wallet. Object personification: a wallet body with
科技前沿
An FDA Panel Just Endorsed These Unproven Peptides
Outside experts—some with a vested interest in peptides—recommended adding a number of the amino acids to the FDA's bulk list, including the “Wolverine stack” touted by Joe Rogan and other influencers.
开源项目
Buzz
Your people, your agents, your project — all in one place Discussion | Link
AI 资讯
Mobileye CEO Amnon Shashua to step aside as company pushes into robotaxis, robotics
Shashua has been invited to take the chairman of the board seat.
AI 资讯
Shipping a Solidity contract to mainnet? Do this 20-minute self-check first
You built something. Tests pass. You're days from mainnet. Before you either skip security entirely (please don't) or spend weeks lining up a full audit, here's a self-check you can run in 20 minutes that catches the mistakes I see most often in first-time deployments. I run security reviews for small and new protocols, and the same handful of issues come up again and again. None of these need a tool — just your eyes and this list. 1. Who can call what? Open every external / public function that moves funds, mints, pauses, or upgrades. For each, ask: should a random address be able to call this? If not — is there an onlyOwner / onlyRole / require(msg.sender == ...) guarding it, in the function itself or in every internal function it calls? The classic bug isn't a missing modifier. It's a function that looks unguarded but delegates to a guarded internal one (fine), or one that looks guarded but the guard is in a branch a caller can skip (not fine). Trace the call, don't trust the signature. 2. The first-depositor trap (if you have a vault) If you mint shares from deposits (ERC-4626 or anything share-based), the first depositor can sometimes donate assets directly to the contract to inflate the share price, so the second depositor rounds down to zero shares and loses funds. Fix: virtual shares, a dead-shares mint at deploy, or a minimum-liquidity lock. OpenZeppelin's ERC-4626 handles this out of the box — a hand-rolled vault usually doesn't. 3. Reentrancy — but only the real kind Not every external call is reentrancy. It's a bug when an attacker-controlled call can re-enter and corrupt shared storage before you've updated it. Quick checks: Do you update state before the external transfer (checks-effects-interactions)? Is there a nonReentrant on functions that move value? Is the call target a trusted, immutable contract, or an arbitrary address the attacker supplies? A call to a protocol-owned contract, or a memory /local variable written after the call, is usually not
AI 资讯
B+tree height after delete: PostgreSQL fast root
Many databases use B+tree indexes, but they all differ. It's a sorted structure. The leaf pages are logically sorted so that a specific key value belongs to one page. A lookup by value reaches a single leaf page and either directly finds an entry for that value or immediately knows there's no entry with that key. When a page becomes full, it is split into two pages, each covering its own dedicated range. To find the right page, an internal page holds the range of values for the pages below. This internal page can become full, and a new level is added above it. Finally, at the highest level, there's a single internal page that is the root. A lookup always starts at the root and goes down to the leaves, following the branches of internal pages. In a traditional B+tree lookup, the cost is proportional to the height of the tree because the search starts at the root and descends to a leaf: 1 page to read when all fits in one leaf that is also the root (0 levels of internal pages, total height is 1). With small keys, this level can typically index hundreds of rows. 2 pages to read when there's one root that can list all leaf pages (1 level of internal page, total height is 2). With small keys, this level can typically index tens or hundreds of thousands of rows. 3 pages to read when there's one level of branches under the root (so 2 levels of internal pages, total height is 3). With small keys, this level can typically index millions of rows. This means that finding one key within ten million rows may require traversing 3 index pages, where most of them are probably in cache given the small number of branches compared to the leaves. For a given index size, whatever the value you are looking for, it's always the same number of pages to read because the index is balanced (the commonly accepted meaning of the B in B+tree). This property is maintained because any page can split, but only splitting the root adds another level. I've described how the height of an index can incr
开发者
I Spent 3 Weeks Debugging Rate Limits Before I Realized the Problem Wasn't My Code
Ever chased a bug for days, only to discover the "bug" was actually the platform working exactly as designed? That happened to me building a client reporting pipeline. The lesson stuck. Here's what nobody tells you about pulling marketing data from multiple ad platforms: the hard part was never the dashboard. It was everything underneath it. The Setup That Looked Simple on Paper The brief sounded easy. Pull spend, clicks, and conversions from Google Ads and Meta. Store it. Display it in a chart. A junior dev could knock this out in a sprint, I figured. Reality disagreed. Google Ads API enforces operation quotas per developer token, and those quotas scale differently depending on account tier. Meanwhile, Meta's Marketing API throttles based on a rolling usage score tied to the ad account itself, not your app. Two platforms. Two completely different throttling philosophies. Neither documented in a way that made the actual limits obvious until you hit them in production. Where Things Actually Broke My first version polled every client account every hour. Fine for three clients. Then we onboarded client number twelve, and Meta started returning 429s intermittently. Not consistently — intermittently. That's the worst kind of bug. I initially assumed it was a code issue. Retry logic, maybe a race condition in my job scheduler. I spent three weeks going down that path. Eventually, I found the real cause: cumulative API call volume across all client accounts was tripping Meta's app-level rate limit, not the individual account limit. The fix wasn't more retries. It was a request queue with exponential backoff, plus a priority system so active dashboards refreshed before idle ones. Simple in hindsight. Expensive in dev hours. The Real Architecture Behind Multi-Platform Reporting If you're building this yourself, here's what a production-grade pipeline actually needs, based on what broke for me. A Queue, Not a Cron Job Don't just fire off API calls on a schedule and hope for t
AI 资讯
I keep finding out about API breaking changes from production errors, so I'm building a changelog watcher
I build products solo. Every single one of them sits on top of somebody else's API — Stripe for payments, OpenAI and Anthropic for AI features, Meta for ads, print-on-demand APIs, map APIs. My code is maybe half of what actually runs in production. The other half belongs to vendors, and it changes whenever they decide it changes. Twice this year the first notice I got about a breaking change was a production error. Not an email, not a warning. An error, and then me digging through the vendor's changelog trying to figure out what they changed and when. The information was public the whole time. It was sitting in a changelog page I never visit, because nobody visits changelog pages until something is on fire. So I'm building the thing I wanted to exist BreakWatch is simple: you tell it which APIs your product depends on, and it reads their public changelogs for you. It fetches each changelog page once a day Diffs it against yesterday's snapshot Classifies the real changes: breaking (endpoint removed, field deprecated, "migrate by September") vs. informational (new feature, docs clarification — stuff you can ignore) Alerts you only when something looks like it will break an existing integration Keeps everything in a searchable timeline, so six months later "what changed on their side right before this broke" takes ten seconds instead of an afternoon No SDK, no credentials, nothing installed in your codebase. It only reads public pages. What I tested this week I ran it against the real changelogs of the ten APIs I'm watching first: Stripe, Twilio, OpenAI, Anthropic, Shopify, GitHub, Slack, Cloudflare, Google Maps and Plaid. Some honest findings: 10/10 scrape cleanly now, but it took fixes. Stripe's changelog page alone is 3.3 MB. SendGrid's standalone changelog doesn't exist anymore (it merged into Twilio's). PayPal's developer site serves a JavaScript shell with an HTTP 404 to anything that isn't a full browser, so it's out until I add rendering. The thing I was most a
AI 资讯
Alexa Plus is getting an AI update to handle more complicated instructions
Amazon is launching an update to its Alexa Plus assistant that will allow it to connect to smart home devices in new ways. With the update, Alexa Plus can link up with tech from Bosch, Delta, Ecovacs, iRobot, Yale Home, Whirlpool, Tapo, Eufy, and others, while automatically routing requests to the correct device. In an […]
开发者
🚀 Turning My Android Phone into a Linux Lab with Termux (Instead of Paying for a VPS)
You don't need a cloud server to start learning Linux administration. A few days ago, I came across a LinkedIn post by Luiz Fernando dos Santos about turning an old Android phone into a Linux server using Termux. Reading his post made me wonder: Do I really need to pay for a VPS just to learn Linux and server administration? The answer, at least for now, seems to be no. Inspired by his idea, I decided to build my own learning environment using nothing but an Android phone and Termux. This article is the beginning of a series where I'll document everything I learn along the way. Inspiration Before getting started, I'd like to give credit to Luiz Fernando dos Santos, whose LinkedIn post inspired this project. His idea of using an old Android phone as a Linux server showed me that I didn't need to rent a VPS to start learning Linux administration. You can check out his original post here: 🔗 https://www.linkedin.com/pulse/transformando-um-android-antigo-em-servidor-luiz-fernando-dos-santos-gu1if/ First Steps I had already been using Termux for quite some time, so the first thing I did was update all the installed packages. apt update apt upgrade -y After that, I installed OpenSSH. pkg install openssh Before moving on, I wanted to understand what SSH actually is instead of just following commands from a tutorial. SSH (Secure Shell) is a protocol that allows you to securely connect to another computer or server through an encrypted connection. It's one of the most common ways to remotely access Linux servers. After installing it, I found the Termux username, configured a password and started the SSH server. Then I tried connecting from my computer... And it worked! It may sound like a simple thing, but seeing my computer remotely access the Linux environment running on my phone was surprisingly satisfying. Improving the Authentication After getting the basic connection working, I started researching how authentication by SSH keys works. I learned that instead of typing a
AI 资讯
Six queries, three runs, every mean 8 — and the fine-tune wasn't why
The bar we set We approved a plan on 2026-07-10 with an acceptance test we weren't sure was reachable. Six drafted analyst-memo queries against Nigerian economic data, scored 0-10 across five dimensions — named-entity density, citation quality, sector-specific detail, honest-gap acknowledgement, decision-usefulness. The strict pass criterion: every query's mean score across three temperature=0.2 runs must be ≥8/10, with no query below 6 in any single run. At approval time the aggregate was somewhere around 30/60 across the six queries — a system that produced grounded but generic answers, and refused competently but not always. The gap to the bar was real. We gave it 4-5 weeks. What we shipped Phase 1 — retrieval breadth. Kind-diversity enforcement across the top-K result set so a "start a fintech" query stopped collapsing into 12 CBN circulars and started pulling BOI, NEXIM, PayStack, Flutterwave, and the World Bank agribusiness chapters in the same context window. Named-entity boost when the query mentions "factory", "startup", "invest", "loan". Deduplication so a briefing about the same fact doesn't crowd out its own primary source. Phase 2 — a six-class rule-based intent classifier and memo templates. Sub-millisecond routing on regex patterns: venture\_feasibility\ , strategic\_forecasting\ , credit\_risk\ , regulatory\_analysis\ , market\_sizing\ , general\_qa\ . Each intent gets a memo template — a section-headed scaffold with a named-entity mandate, an honest-gaps section, and a 1000-1500 word target. The general\_qa\ template stays empty (no memo shape) so genuinely-general questions don't get forced into a memo they don't need. Phase 3 — composition quality. Two changes did most of the work here: 1. A CITATION PREFERENCE: PRIMARY OVER BRIEFING\ block in the system prompt. Primary sources — CBN circulars, NAICOM regulations, NBS reports, textbook chapters, IMF Article IV, press coverage of specific events — get cited over daily briefings when both are presen
AI 资讯
Why I Chose Slot Hashes Over VRF for Fair Random Selection on Solana
When I set out to build a provably-fair random selection system on Solana, the obvious choice for randomness was a VRF (Verifiable Random Function). Instead, I built the system around Solana's SlotHashes sysvar with a commit-reveal scheme. Here's why, and what I gave up to get there. The problem A fair-selection system needs a winner (or set of winners) chosen in a way that's fair, and just as important that participants can check for themselves without taking anyone's word for it. VRF services (Switchboard, ORAO, etc.) solve the fairness part well: they produce randomness that's unpredictable in advance and cryptographically provable after the fact. But they come with a dependency on an oracle, a fee per request, and a proof that most users will never actually verify they'll trust it because the crypto math says they can, not because they did. I wanted something a participant with no crypto background could check in a browser console. The approach: commit-reveal with slot hashes The core idea: commit to the participant list before you know the randomness, then derive the randomness from a slot hash you couldn't have predicted at commit time. rust fn derive_randomness(target_hash: &[u8; 32], participant_root: &[u8; 32]) -> [u8; 32] { let mut combined_seed = [0u8; 64]; combined_seed[..32].copy_from_slice(target_hash); // slot hash at reveal combined_seed[32..].copy_from_slice(participant_root); // Merkle root, locked at commit solana_keccak_hasher::hash(&combined_seed).to_bytes() } The flow: Commit: participant list is finalized and hashed into a Merkle root; this is written on-chain. Wait: a target slot in the future is chosen as the reveal point. Reveal: once that slot passes, its hash is pulled from SlotHashes and combined with the committed root to derive the randomness. Select: the randomness deterministically picks winners from the participant set; winners get their own Merkle root and proofs. Every draw ends up with an audit record like: rust pub struct AuditR
AI 资讯
houhou — Resilience Policies for TypeScript Async Functions
The problem Most resilience libraries in the TypeScript ecosystem are tied to HTTP clients (axios-retry, p-retry, Polly.js) or require wrapping your function in a class with a .execute() ceremony. What if you just want to wrap any async function — a database query, an internal service call, a file operation — with retry logic, a timeout, and a circuit breaker, without pulling in heavy dependencies? Enter houhou . What is houhou? Houhou is a zero-dependency TypeScript library (~500 LOC) that wraps any async function with composable resilience policies. The wrapped function keeps the exact same signature — you call it like the original. import { task } from ' houhou ' const charge = task ( chargeCard ) . retry ( 3 ) . timeout ( 10 _000 ) . fallback (() => ({ status : ' pending ' })) await charge ( account , amount ) Policies at a glance Retry Re-execute on failure with fixed or exponential backoff: task ( fetchUser ). retry ( 3 ) // shorthand task ( fetchUser ). retry ({ attempts : 5 , backoff : ' exponential ' , jitter : true , delay : 500 }) Timeout Reject if the function doesn't complete in time: task ( fetchUser ). timeout ( 5000 ) Fallback Run an alternative function on failure: task ( fetchUser ). fallback (() => loadFromCache ( id )) Circuit Breaker Prevent repeated calls to an unhealthy service: task ( queryDb ). circuitBreaker ({ failureThreshold : 5 , successThreshold : 2 , resetTimeout : 30 _000 }) Delay Wait before execution: task ( syncData ). delay ( 1000 ) Policy ordering matters Policies are nested : the last method called wraps the previous ones. Execution order is reverse of declaration order. task ( fn ). retry ( 3 ). timeout ( 1000 ) // → timeout wraps retry // → function runs → retry on failure (up to 3 times) → 1s total timeout // → if the timeout fires, there are no more retries task ( fn ). timeout ( 1000 ). retry ( 3 ) // → retry wraps timeout // → function runs → 1s timeout → if timeout fires, retry catches it // → the whole cycle repeats up