v0.9.3 — Mobile/compact UI rework, Manifest provider preset, oversized-tool-result poisoning fix, Telegram auto-reconnect
Features
-
Mobile-first compact mode — Renderer reworked so the same Electron React tree adapts cleanly to narrow viewports. Primarily targeting the WebUI delivery, but also lights up automatically in the desktop app whenever the shell is resized below 768px wide. Highlights: (a)
isAutoCompactderived from@container/shellwidth — single-panel iOS-style drill-in between the navigator (session list / sources / etc.) and the focused content panel; sidebar + right rail auto-hide; layout runs flush to the viewport edges. (b) Mobile-firstAppMenuwith a full-screen nav stack (MobileAppMenu) extracted from the existing component; desktop branch (DesktopAppMenu) keeps the previous behaviour verbatim. (c) Touch-first pickers — workspace switcher, source picker, permission-mode selector, and session-list filter all swap from Radix popovers to vaulDrawerbottom sheets in compact mode (Radix portals out of the panel container query, so popovers were getting clipped on narrow viewports). (d) Top bar reorganised — workspace pill on the left, action chips collapse,+ new chatFAB pins to the viewport bottom (portalled out of the navigator transform soposition: fixedresolves to the viewport, not the transformed ancestor). Existing CSS uses container queries throughout, so the desktop layout (≥768px shell) is structurally unchanged. PR #363. (83cc0cca,3a63cbe1,1640b181,90e75c7b,e6e3247a,e24d4062,7f2627ab) -
Manifest as a default API-key provider preset — Adds Manifest (OpenAI-compatible endpoint at
https://app.manifest.build/v1) to the API-key provider presets alongside HuggingFace, Vercel AI Gateway, and the other OpenAI-compat options. Routes through the same path as thecustompreset (pinscustomEndpoint.api='openai-completions', skips Pi model discovery, resolvespiAuthProvider='openai'); a newOPENAI_COMPAT_CUSTOM_URL_PRESETSset captures this category so future non-Pi-SDK OpenAI-compat providers can be added with a single entry. Default model field is seeded withautoto match Manifest's routing model — andhandleBaseUrlChangenow seeds the same default when the user types amanifest.buildURL directly (previously only the click path seeded the model). Connection list shows "Manifest" name + Manifest favicon + branded subtitle. External contributor: @guillaumegay13 — originally lukilabs/craft-agents-oss#731, lifted via PR #367 with two follow-up review fixes folded in. (e16d28f1,c627480d,4db12f08)
Improvements
-
GHCR + workflow rename:
lukilabs→craft-ai-agents— Cuts the GitHub Container Registry image namespace and all workflow/script references over to the new GitHub org. Marketing site GitHub links also updated. No user-visible config change for app users; impacts anyone pulling docker images or referencing the OSS sync scripts. (8ee72a47,99dd5e35,f040d555) -
Repo-wide i18n string-scan lint — New
lint:i18n:stringsscript scans the entire repo for hardcoded English strings that should go through i18n;lint-i18n-staged.shwas trimmed in the same commit since the per-file scan is now subsumed by the repo-wide one. Helps prevent future strings from skipping the i18n pipeline. (013674e0) -
Settings icons consistency cleanup —
SettingsIcons.tsxcollapsed by ~180 lines to share one icon style across settings sections; smallmodels-pi.tstouch-up alongside. Contributed by Balint Orosz. (da685016) -
Messaging access channel routing — 9 messaging access-control channels (
messaging:access:{getOwners,setOwners,getMode,setMode,getPending,dismissPending,allowPending,setBindingAccess}+messaging:pendingChanged) added toREMOTE_ELIGIBLE_CHANNELS. They were declared inchannels.tsduring the recent Telegram permission-buttons / access-control work but never registered inrouting.ts— the exhaustiveness test was failing on this branch base. (9b694d8d)
Bug Fixes
-
Session poisoning from oversized tool results — A single
Readof a base64-heavy file could push a session past the model's context window, after which every retry returnedinvalid_requestand the only "fix" was abandoning the session. Two compounding bugs fixed: (a) Thechars/4token estimator under-counts dense base64 by ~25%, so the existing 15K-token spill threshold fired too late; newestimateTokensDensityAware()switches tochars/1.5when ≥70% of a ≥20KB tool result is inside unbroken base64-charset runs (≥100 chars), andTOKEN_LIMITis lowered to 12000 for additional headroom. (b) Theinvalid_requestUX actively misled users — the hardcoded fallback advice ("Try removing any attachments / check if images are in supported format") fired regardless of what was sent or what the API said, because both real-error sources are dead on the Claude SDK path (parseApiErrorFromDebugLogreads a file the SDK no longer writes; the network interceptor is Pi-only). Now: split overloadedONE_M_CONTEXT_HINTSinto 1M-tier-specific (context-1m/tier) and generic context-overflow (context window/prompt is too long/…); generic overflows route to a new "Context Window Exceeded" error with/compact+ new-session advice; "remove attachments" hints only fire when the API message actually mentions image/attachment/media/format or the just-sent user turn included attachments (newuserTurnHadAttachmentsfield threaded throughClaudeSdkErrorContext); when neither error source provides detail, append a pointer to~/Library/Logs/@craft-agent/electron/main.log. PR #365. (636b0b67,fad9cbb3) -
Telegram polling auto-reconnect after failure — When Telegram polling stopped (most commonly a 409 Conflict from two app instances sharing the same bot token),
connectedstayedfalsepermanently and every subsequent session event routed viatelegramTopicautomations was silently dropped — bindings were created, sessions ran, responses were generated, but nothing ever reached Telegram.TelegramAdapternow callsscheduleReconnect()on any polling failure with exponential backoff: 30s base for 409 (gives the competing process time to exit), 5s for other errors, capped at 5 min.destroy()cancels any pending timer so intentional shutdowns don't re-enter the loop.MessagingGateway.onSessionEventalso now logs awarn(adapter_not_connected) instead of silently continuing, so future disconnects surface immediately inmessaging-gateway.log. PR #366. (28f1082d) -
WhatsApp voice messages / audio attachments now delivered — WhatsApp worker was silently dropping audio attachments instead of forwarding them to sessions. The worker now emits audio/media attachments correctly. Closes #719. (
84d6d0fd) -
source_testforwards OAuth/bearer tokens to the MCP probe — Connection probe was reportinginvalid_tokenfor OAuth MCP sources whose live runtime succeeded with the same source —source_testwasn't passing the access token to the probe. Fixed: the access token is now forwarded so the probe matches live-runtime auth. Closes #720. (0dd6eeef) -
Telegram permission buttons idempotent + clear keyboard on resolution — Permission buttons could double-fire on rapid taps; the inline keyboard also persisted after the permission was resolved. Buttons are now idempotent and the keyboard is cleared once the permission is granted/denied. Closes #726. (
9340b2b5) -
Model picker exposes connection switcher on empty session for single-model
pi_compatdefault — When a workspace's default connection was api_compatconnection with only one model, the model picker hid the connection switcher entirely, making it impossible to switch providers from an empty session. The picker now exposes the connection switcher even in this case. Closes #727. (7faf9fc4) -
Windows build: sparse-checkout cone mode + illegal-character filename rename — Two issues blocking Windows CI: (a) sparse-checkout was using non-cone mode, which doesn't apply on Windows; switched to cone mode. (b)
Hermes - Anna: thinking.pdffrom the project-evolution archive contained:, illegal on Windows; renamed. Project-evolution PDFs are also excluded from the Windows checkout entirely. (5cc83b86,bf187448,28741005) -
Compact-mode polish (multi-commit) — A series of small fixes uncovered during compact-mode bring-up: React #300 (Maximum update depth) when toggling
isCompacton resize (227f3486); new-chat FAB pins to viewport bottom via portal soposition: fixedresolves correctly, and is hidden when a chat is open (406ece09,3ce4ffb3); PanelHeader title centered across full panel width (7d81e6a7); polish on toggles + topbar sizing for narrow viewports (060ae0d1); compact mobile header controls polish (74c52244). -
AppMenu: drop history bridge + Keyboard Shortcuts row in compact — The menu had a history bridge that rolled back menu navigations on back-button press; removed (
390d4eeb). The Keyboard Shortcuts row was also dropped from the mobile menu since it's irrelevant on touch (b38b1c05). -
Docs: correct path to Mid-stream sends setting — Documentation referenced an outdated path. Fixed. (
97f58dec)
Breaking Changes
- None. All changes are backward-compatible. NEW DOCKER IMAGE -> ghcr.io/craft-ai-agents/craft-agents-server
Notes
-
External contributors in this release:
- @guillaumegay13 (Guillaume Gay) — Manifest provider preset (originally lukilabs/craft-agents-oss#731, lifted as #367)
-
Repo namespace migration: GHCR images and workflow references moved from
lukilabs→craft-ai-agents. If you pullghcr.io/lukilabs/craft-agents-server, switch toghcr.io/craft-ai-agents/craft-agents-server. The OSS issue tracker stays atlukilabs/craft-agents-oss(issues#719,#720,#726,#727linked above). -
Mobile/compact mode: triggers automatically when the shell width drops below 768px, regardless of whether you're in the desktop app or WebUI. Resize a desktop window narrower to try it.
-
Token-estimator change: the new density-aware spill threshold should keep most context-window crashes from happening in the first place. If you previously saw
invalid_request: remove attachmentsafter aReadof a binary-heavy file without an actual attachment, that error is gone — context overflows now route to a separate "Context Window Exceeded" error pointing to/compact.