July 10, 2026
40 commits: image search shipped, the API rewrite goes wide
Chalie can now find and verify images on the open web, and the new Endpoint/Action contract from last week ate nearly every API vertical in a single day.
Forty commits. Two things actually happened: a real feature shipped end to end, and the new API base contract stopped being an idea and became the way almost everything talks to the database now.
Start with the feature, because it’s the one you’ll actually notice. Chalie can now search the web for images, not just text. Ask for a query, get back deduplicated results with a thumbnail, a source, and a title — and if a vision provider is configured, each image gets independently checked against what you actually asked for, not just what a search engine thinks matches the keywords. An unverified badge means no vision provider was set up to check; it doesn’t mean the result is wrong, and the card says so rather than pretending. Every image URL gets pushed through a strict allowlist parse before it’s ever rendered or downloaded — no relative paths, no scheme tricks, http and https only. Open-web content earns that kind of paranoia by default. The card itself went through a redesign mid-build too: dropped the frame, matched the layout the weather card already uses, turned the thumbnail strip into a proper swipeable carousel instead of a static grid. Small taste call, but it’s the difference between a bolted-on feature and one that looks like it belongs.
The bigger story is the migration. Capabilities, memory search, personality, voice settings, the scheduler, providers, MCP clients, subagents, skills, policies, lists, wrappers — every one of those moved off its own hand-written Flask namespace and onto the shared Endpoint/Action contract, and the legacy modules got deleted, not deprecated. That’s the difference between “we have a new pattern” and “the old pattern is gone.” Along the way the contract itself got sharper: swagger docs now only claim the status codes a handler can actually emit — no more promising a 404 that’s structurally impossible — and an unknown /api/* path now fails loud with a JSON 404 instead of quietly serving the frontend’s index.html and pretending the request succeeded. That second one is the kind of bug that only shows up when your backend and frontend drift apart, and every client would have silently swallowed the mismatch.
One migration slice needed a genuine rewrite underneath it, not just a reshuffle. The scheduler used to store its cron shape as three separate integer columns, each one nullable-means-“every” — which can’t express */5 or a weekday list or a real range. That’s gone, replaced with an actual five-field crontab engine, parsed and matched the way cron has worked since Vixie wrote it in the 80s. The schedule ability and the UI both went from a handful of hardcoded options to the real thing.
Not everything was new work landing clean. One fix patched a half-finished commit from earlier in the API rewrite — a module got deleted while the import rewrites that depended on it hadn’t been committed yet, which would’ve left the whole ability registry unable to import. Caught and closed out before it went anywhere near a release. That’s the risk of moving this fast across this many verticals at once: the failure mode isn’t a subtle bug, it’s the app not booting.
What’s left of the legacy API surface after today is small. The next pass is almost certainly finishing that off, then turning attention back to the thread-system work that got a DOM-contract fix riding along today — shared websocket envelope keys so tool-call frames stop hand-rolling their own serialization.
-
Shipped image search end to end: DDG-backed results, per-image vision verification, and a redesigned frameless carousel card
-
Migrated capabilities, memory search, personality, voice settings, scheduler, providers, MCP clients, subagents, skills, policies, lists, and wrappers onto the Endpoint/Action contract, deleting every legacy module
-
Swagger docs now only claim status codes a handler can actually emit, and unknown /api/* paths fail loud as JSON 404 instead of silently serving the frontend shell
-
Rebuilt the scheduler on a real five-field crontab engine, replacing three nullable integer columns that couldn’t express ranges, lists, or day-of-week
-
Caught and fixed a half-landed commit that would have left the ability registry unable to import