-
Notifications
You must be signed in to change notification settings - Fork 859
Releases: craft-ai-agents/craft-agents-oss
v0.10.3
v0.10.3 — Claude Fable 5
Features
- Claude Fable 5 is here — Anthropic's most capable widely released model (GA 2026年06月09日) is now available in Craft Agent on the Claude Agent SDK path, with a full 1M-token context window. It works across direct Anthropic connections and AWS Bedrock (us/eu/global inference-profile variants), with the model description localized in all 7 languages. Opus 4.8 remains the default — Fable is offered alongside it, not in front of it — and it is added to the Pi → Anthropic auth-bridge preferences below Opus. Because Fable 5 (and the Mythos 5 class) run with adaptive thinking always on and reject an explicit
disabledthinking option, the thinking resolver now maps the "off" and minimize-thinking cases to low-effort adaptive thinking for these models; Opus, Sonnet, and Haiku behavior is byte-for-byte unchanged. (22c7ae99)
Improvements
- Claude Agent SDK uplifted to 0.3.170 —
@anthropic-ai/claude-agent-sdkmoves from 0.3.154 → 0.3.170 (root plus the core and shared peer dependencies, lockfile refreshed). No API breakage; the full typecheck passes clean. (22c7ae99)
Bug Fixes
- None.
Breaking Changes
- None.
Assets 5
v0.10.2
v0.10.2 — Link labels, Anthropic account visibility, and prompt-cache fixes
Features
-
Link label value type — Labels can now carry a
linkvalue alongside the existing string/number/date types. A link value renders as a clickable chip (the URL scheme is stripped for display) that opens in your external browser, with a keyboard-accessible "Open link" action in the value popover. The type is threaded through the zod schema, CLI, and agent-prompt layers, and the clickable value appears consistently in the session list, chat display, and chat-input badges. Documented inlabels.mdwith validate/format/resolve tests. (8aae50ad) -
Resolved Anthropic account & org shown per OAuth connection — Settings → LLM Connections now displays the real Anthropic identity ("email · org") each Claude OAuth grant resolves to, captured from the token-exchange response and persisted on the connection. When two connections in the same workspace resolve to the same Anthropic account (shared quota — a previously invisible mis-route), an amber warning is surfaced. This also fixes a load-bearing bug where
updateLlmConnectionrebuilt connections from a hardcoded field allowlist and silently dropped any unlisted field on every save; the identity fields are now preserved with a round-trip test. Fixes #838. (a4b8868e) -
Last sent message restored to the input on Stop — Clicking Stop (an explicit cancel, not a redirect or new send) now copies your last sent message back into the chat input so you can tweak and resend it. It is append-safe: a draft typed while the agent was running is preserved, and the restored text is appended below it. Queued, never-sent messages continue to be handled by the existing restore path. (
cfbe802b)
Improvements
- None.
Bug Fixes
-
Pi prompt-cache no longer busts every turn — The Pi adapter folded volatile context (minute-precision date/time,
session_state, source state) into the system prompt, which pi-ai caches as the prefix ahead of all history — so re-stamping it each turn droppedcacheReadto 0 every turn (notably with non-Claude models such as DeepSeek V4).PromptBuilder.buildContextPartsis now split intobuildVolatileContextParts+buildStableContextParts; the Pi path keeps only stable blocks (workspace capabilities, working directory) in the cached prefix and routes volatile blocks to the user-message tail, mirroring what the Claude path already did. The Claude path stays byte-identical. Fixes #862. (80b478e3) -
Accept-Plan chevron rotates when the dropdown opens — Both Accept-Plan triggers render via Radix
asChild, sodata-state="open"landed on the host<button>, never on the nested chevron<svg>— the self-scopeddata-[state=open]:rotate-180never fired. The rotation is now scoped to a named group on the button (group/accept+group-data-[state=open]/accept:rotate-180). Fixes #840. (f97f483b)
Breaking Changes
- None.
Assets 5
v0.10.1
v0.10.1 — Claude Opus 4.8 default, localized session titles, and Apple Silicon–only macOS builds
Features
- None.
Improvements
- Claude Opus 4.8 is now the default Opus model — Upgraded the Claude Agent SDK to 0.3.154 and set Claude Opus 4.8 as the default Anthropic Opus model, while keeping Opus 4.7 selectable. The deprecated Opus 4.6 is removed from the model pickers. Existing direct-Anthropic Opus 4.7 defaults move to Opus 4.8, and Opus 4.5/4.6 selections migrate to the best available current Opus model. Bedrock mappings, Pi fallback handling, model-migration tests, docs, and UI examples were updated to match. (
0a1357b2)
Bug Fixes
-
Session titles honour the Settings → Appearance language — The main-process i18n instance has no language-detection plugin (there is no
localStoragein Node) and was never re-hydrated from disk, so it always sat at theenfallback after restart. Title generation (generateTitle/refreshTitle) and the system prompt's "Preferred language" line read that main-process language, so they came back in English even when Appearance was set to another language. The chosen UI language is now persisted to an internal, validateduiLanguagefield and the main-process i18n is hydrated from it on startup; the renderer also pushes its resolved language to main once on upgrade so the value is learned without re-picking. Partially addresses #815 and #738. (4a743ef2) -
Text-selection highlight stays aligned when scrolling a preview — Selecting text inside a scrollable
markdown-previewblock (themax-h-[400px] overflow-autowrapper) and adding a follow-up left the yellow range highlight and its numbered chip anchored to the original coordinates, so they drifted away from the text as the block scrolled. A capture-phase scroll listener now recomputes the overlay geometry on any nested scroll — a coords-only fast path coalesced withrequestAnimationFrame, with a no-op short-circuit when there are no annotations — keeping the highlight pinned to its text. The same fix is mirrored in the viewer's annotation path. (14bba50c)
Breaking Changes
-
macOS Intel (x64) builds discontinued — Apple Silicon only — Craft Agents for macOS now ships an Apple Silicon (arm64) build only; Intel-based Macs are no longer supported. The x64
.dmgis no longer produced (v0.10.0 was the last release with an Intel build). Apple Silicon (M-series) Macs, Windows, and Linux are unaffected. -
Legacy
languagepreference field removed — The free-text, user-editablelanguagefield inpreferences.jsonis replaced by an internal, validateduiLanguagefield driven by Settings → Appearance. Existing config files are tolerated (the schema is passthrough) and the stalelanguagekey is scrubbed on read, so no user action is required. Theupdate_user_preferencestool no longer accepts alanguageargument.
Assets 6
v0.10.0
v0.10.0 — Remote browser_tool bridging, per-workspace browser tab isolation, and #824 basic-auth fix
Features
-
Remote
browser_toolbridged into the user's local Electron browser — Agents running on a remote workspace (headless server, docker, WebUI) can now drive the user's local desktopBrowserPaneManagerend-to-end. Adds aclient:browser:invokeWS capability (advertised on handshake, server invokes client, plainErrorwith.codepreserved through both directions), mirroring howshell.openExternalalready works forOPEN_URL. Transport gainshasClientCapability/findClientsWithCapabilityfor routing; Electron gets a new__browser:invokeIPC dispatcher with per-method owner-key authorization, no-manual-window-reuse for remote callers, session-scopedlistInstances, and screenshotBuffer↔Uint8Arrayconversion across the wire.server-coregets aRemoteBrowserPaneManager(session-boundIBPMimpl) andSessionManager.getBrowserPaneManagerForSessionwith capability-aware host-client fallback and per-session pin cleanup on disconnect.uploadFileis blocked over the bridge;evaluateis gated by a localallowRemoteEvaluatesetting. The Pi runtime learns friendly error mappings forBROWSER_NO_CAPABLE_CLIENT,CAPABILITY_UNAVAILABLE,CLIENT_DISCONNECTED,CLIENT_REQUEST_TIMEOUT,BROWSER_INSTANCE_NOT_OWNED,BROWSER_REMOTE_UPLOAD_NOT_SUPPORTED, andBROWSER_REMOTE_EVALUATE_BLOCKED, and now mirrors Claude'sgetBrowserToolEnabledgate so Pi no longer advertisesbrowser_toolwhen the toggle is off. 27 new tests cover wire packaging, per-method authz, capability introspection, host-client fallback, screenshot round-trip, error-code preservation, and the Pi error-mapping contract. (1d926c33) -
Browser tabs isolated per workspace —
BrowserPaneManageris process-global andSTATE_CHANGEDused to broadcast{ to: 'all' }, so a chat in workspace A saw browser tabs and status banners owned by sessions in workspace B. EveryBrowserInstance(and theBrowserInstanceInfoDTO) now carries a nullableworkspaceId;STATE_CHANGEDroutes to{ to: 'workspace', workspaceId }when set (falling back to{ to: 'all' }for unbound manual windows); thebrowserPane.LISThandler filters byctx.workspaceId; and the renderer reads a newbrowserInstancesForWorkspaceAtomFamilykeyed byactiveWorkspaceId. Windows still run in parallel as realBrowserWindows — this is a UI visibility filter, not a sandbox.REMOVED/INTERACTEDstay broadcast-to-all (id-only payloads, harmless no-op on workspaces that never saw the entry).workspaceIdships optional on the DTO, so old renderers tolerate missing values (treatsundefinedasnull→ passes the filter, equivalent to today's behavior). 17 new tests across atom filter, BPM stamping, and broadcast/LIST routing. (af817192)
Improvements
-
markdown-previewblock documented in the rich-output reference — Themarkdown-previewblock (shipped in v0.9.6) is now covered inapps/online-docs/go-further/rich-output.mdxalongside the existinghtml-preview/pdf-preview/image-previewentries, so users discovering the block in-chat can find usage examples and thesrc/itemsfield reference in the docs. (2d9693b1,70c2955f) -
Browser-bridge wiring is observable from server logs — Three
sessionLog.infolines inSessionManagernow confirm whethersetRpcServerran at bootstrap and whether the browser-pane-forwarding block executed at agent init. Makes it possible to diagnose remote workspaces that still hit "Browser window controls are not available" from server logs alone, without attaching a debugger. (ad26e61d)
Bug Fixes
-
Renderer accepts both local AND remote workspace ids when filtering tabs — When connected to a remote workspace, a renderer has two relevant workspace ids:
activeWorkspaceId(the LOCAL Craft Agents window's identity, used for locally-opened manual tabs) andactiveWorkspace.remoteServer.remoteWorkspaceId(the REMOTE server's id, used by the remote agent when it stamps tabs through the WS bridge). The first iteration of the workspace-isolation filter only matchedactiveWorkspaceId, so tabs stamped with the remote id (which is what every remote agent-opened browser carries) got filtered out — the TopBar tab strip and the toolbar status badge became invisible for remote browsers, and opening one then hiding it left it inaccessible. Replaced theatomFamilywith a plain helperfilterInstancesForWorkspace(local, remote): tabs match if either id matches (or ifworkspaceIdis null/undefined for back-compat). (bf8429fa) -
STATE_CHANGEDbroadcasts to all clients again; the visibility filter lives in the renderer — The Phase-4 server-side workspace filter (on bothSTATE_CHANGEDrouting and theLISThandler) was wrong for remote-mirror workspaces: a renderer's transport-levelworkspaceIdis the LOCAL window's identity, while remote-bridged browser tabs carry the REMOTE server's workspace id. The two never match, soSTATE_CHANGEDtargeted at the remote id got dropped by the WS routing layer (no local renderer reports itself as being in the remote workspace) andLISTreturned empty for the same reason. Workspace isolation now lives entirely in the renderer (filterInstancesForWorkspace), which knows both ids viaactiveWorkspace.remoteServer.remoteWorkspaceId. The handler reverts to{ to: 'all' }broadcasts and a fullLISTresponse. Privacy is unchanged — every locally-connected renderer belongs to the same user — and remote tabs finally show up in the TopBar of the workspace that owns them. (f831bb42) -
No window reuse on the remote-bridge lifecycle path (closes the cross-workspace hijack) — The capability dispatcher correctly set
allowReuseManual=falseforcreateForSessionbut flowedgetOrCreateForSessionandfocusBoundForSessionthrough the public helpers, which defaultallowReuseManual=true. The remote agent'sbrowser_tool openmaps tofocusBoundForSession, so it could adopt an unbound window left behind by a local session — exactly the cross-workspace hijack theworkspaceIdfilter was meant to block. TheworkspaceIdfilter still helps but it's best-effort: windows created before workspace stamping (or via paths that never setworkspaceId) haveworkspaceId=nulland remain universally adoptable. Belt-and-brace fix: every remote lifecycle call now passesallowReuseManual=false, so remote sessions always create fresh windows unless they already own one. (ce3340a1) -
Unbound-window reuse scoped to the owning workspace (no more "tab moved from workspace A to B") — When a session's turn ends,
unbindAllForSession()clearsboundSessionIdand flipsownerTypeto'manual'so the next turn of the same session can re-bind the window; theworkspaceIdstamped at creation is preserved.findReusableUnboundInstance()was matching ANY unbound 'manual' window regardless of workspace, so a session in workspace B would happily pick up the leftover window from workspace A —bindSession()would then overwriteworkspaceIdto B, effectively "moving" the window from A to B and making workspace A's tab strip lose the entry while B's gained it. Reuse is now allowed only when the candidate'sworkspaceIdis null (truly user-opened manual window — adoptable by anyone) or matches the caller'sworkspaceId. Same-workspace next-turn reuse still works. (ceb24603) -
TopBar-opened manual windows inherit the workspace they were opened in — The
browserPane.CREATEhandler created manual windows withworkspaceIddefaulting tonull, which the workspace-isolation filter intentionally treats as "visible to all workspaces" — so a TopBar-opened tab leaked into every workspace's tab strip. The renderer that firesCREATEalways hasctx.workspaceIdset, so the handler now passes it through tocreateInstance/createForSession. CLI / agent-harness callers with no workspace context (ctx.workspaceId === null) still get the broadcast-to-all behavior as a safe fallback. (7dfcaeac) -
BrowserInstanceprojected to a plain snapshot before IPC return — The localBrowserPaneManager.getInstance(id)returns the liveBrowserInstancewhich embeds Electron native references (window: BrowserWindow,pageView: BrowserView, ...). When the__browser:invokedispatcher returned that object over IPC, Electron's structured-clone serializer threwAn object could not be clonedand the remote agent's logging-sidegetInstanceAsynccall failed. AddedtoSnapshot(instance)that emits only theIBPM-declared fields (ownerType,ownerSessionId,isVisible,title,currentUrl) and routed the dispatcher'sgetInstancebranch through it. (8e2534b5) -
source_testbase64-encodes basic-auth credentials —testApiConnectionWithAuthwas interpolating the raw vault value into theAuthorizationheader, so basic-auth sources gotBasic {"username":"...","password":"..."}and 401'd against every provider. The vault storessource_basiccredentials as JSON (written bysource_credential_prompt/ the WebUI); the runtime path inapi-tools.tsbuildHeadersalready parses and base64-encodes — the validator path was just left out. Now parses the token as JSON when it hasusername+password; falls through to pass-through behavior for legacy / hand-edited base64 entries and any non-JSON string, mirroringbuildHeaders(). Three regression tests cover the JSON form, the legacy already-encoded form, and a non-JSON garbage token. Fixes #824. (96dd7c0d)
Breaking Changes
- None. The
workspaceIdfield on theBrowserInstanceInfoDTO is optional, so older renderers and older agents tolerate missing values (treatsundefinedasnull→ passes the visibility filter, equivalent to pre-0.10.0 behavior).
Assets 6
v0.9.6
v0.9.6 — Auto-update window restoration, mid-session credential refresh, and #807/#798/#804 fixes
Features
-
Workspace name in window title when multiple windows are open — With one window open the title stays as the app name ("Craft Agents"). Open a second window and each window's title becomes the workspace name it belongs to, so windows are trivial to tell apart in Cmd-Tab, Mission Control, and the Windows taskbar. The renderer's static
<title>is suppressed so it can't clobber the main-process title. (5a49b6ca) -
Inline
markdown-previewblock for rendering.mdfiles — A newmarkdown-previewcode-block type mirrorshtml-preview/pdf-preview/image-preview: reference a.mdfile by absolute path and the chat renders it through the shared Markdown component, with adisablePreviewBlocksguard that preventsmarkdown-preview-inside-markdown-previewrecursion without disabling other nested preview blocks. Registered in bothminimal(assistant chat) andfullmodes; supports multi-itemitemsarrays. Partially addresses #807. (45760a6f)
Improvements
-
Online-docs coverage for multi-window titles and auto-update restoration —
apps/online-docs/go-further/workspaces.mdxnow documents the new window-title behavior and the auto-update window-state preservation, so users discovering these features in-app can find rationale and edge-case notes in the docs. (755a8b77) -
Online docs introduction polish — Tightened wording in the getting-started introduction (Mintlify dashboard edit). (
3a5378db) -
Messaging gateway docs reflect the 0.9.5 fallback fix — Clarifies that
progressandfinal_onlymodes now both fall back to the most recent assistant text when a run ends on a tool call without a non-intermediatetext_complete, instead of leaving a thinking bubble (progress) or staying silent (final_only). Genuinely empty runs are still silent. (0cfd1ffb)
Bug Fixes
-
Multi-window state survives auto-update — electron-updater (Squirrel.Mac) destroys all BrowserWindows between
quitAndInstallandbefore-quitfiring, so the existing window-state save ran with an empty snapshot and clobbered~/.craft-agent/window-state.jsonwith{ windows: [] }. Users lost their multi-window setup every time they accepted an update.installUpdatenow fires asetBeforeUpdateQuitHookcallback that captures and saves window state while windows still exist, and the latebefore-quitpath adds an empty-snapshot guard so the pre-update save can't be overwritten. (3db842e0) -
API source credentials refresh mid-session — Sources with bearer/header/query/basic auth captured the credential as a static string at tool-creation time. After refreshing an expired token via
source_credential_prompt, the in-process tool kept sending the stale value (401 until full session restart) even thoughsource_testconfirmed the new token worked. Non-OAuth API sources now route through a credential getter that reads the vault on every call, mirroring the existing OAuth / renew-endpoint path. OAuth and renew-endpoint sources are untouched — they already have refresh viaTokenRefreshManager. (5b6a0588) -
Stale
source_apikeycredential no longer leaks when flipping authType to'none'—SourceCredentialManager.getCredentialId()maps'none','header', and'query'to the samesource_apikeyslot. Flipping a source from a credential-bearing authType to'none'left the stored credential addressable under that slot, where it could later overridedefaultHeaderson a rebuild — the bare credential value got sent as a Cookie header instead of the newdefaultHeaders.Cookievalue.saveSourceConfignow best-effort-deletes thesource_apikeyslot when the new config is an API source withauthType:'none'. Cleanup never throws and never blocks the config write. (d0c70f23) -
Blocked URL schemes now explain why + DOM
hrefs are sanitized — When react-markdown'sdefaultUrlTransformstripped afile:/javascript:URL to empty, the anchor handler fell back to anchor text andnew URL(text)rejected it as "Invalid URL" — a generic toast with no rationale.DANGEROUS_SCHEMESis now aMap<scheme, reason>, the reason flows through theOPEN_URLhandlers in bothserver-coreand the Electron GUI, and the error message now reads e.g.URL blocked (file:). file: URLs are blocked because shell.openExternal can launch local executables on Windows.... The DOMhrefattribute is also sanitized throughdefaultUrlTransformand set toundefinedfor dangerous schemes, closing the middle-click / cmd-click escape route through Electron'ssetWindowOpenHandlerandwill-navigate. Fixes #807 (URL handling part). (746ebb34) -
cache_control1h TTL ordering bug and over-broad "tool not supported" classifier — Two bugs surfaced together whenextendedPromptCachewas enabled on an Anthropic connection at session start. (1)upgradePromptCacheTtlwalked system + messages + top-levelcache_controlbut skippedbody.tools. Anthropic processes blocks in ordertools → system → messagesand rejects requests wherettl='1h'appears afterttl='5m', so a stale 5m on any tool producedsystem.0.cache_control.ttl: a ttl='1h' cache_control block must not come after a ttl='5m' cache_control block. Tools are now walked first in both the upgrade and disabled-strip paths. (2)parseErrormisclassified the same 400 as "Model Does Not Support Tools" because the heuristic fired on the API's hint string mentioningtools. The overly broad pattern is dropped and a finalinvalid_request_error / 400branch routes generic Anthropic 400s toinvalid_requestinstead ofunknown_error. (26e6e675) -
Mobile WebUI send button stays visible when the model name is long — The compact bottom-bar layout had every left-side item as
shrink-0with no overflow guard on the outer row, so a long custom-endpoint model name on a 375 px viewport overflowed the row and pushed the send button off screen.CompactModelSelector's trigger is now shrinkable with amin-w-[64px]tap-target floor, and the compact bottom-bar children are wrapped in their ownmin-w-0 shrink overflow-hiddengroup so the model label truncates first and the send button stays anchored to the right. Fixes #798. (5e95e72e) -
Headless server auto-retries
source_activatedlike the Electron renderer did — The[<slug> activated]re-send moved intoSessionManager.processEventso headless deployments (WebUI, docker server) chain source activations the same way the Electron renderer did. The renderer'sauto_retryeffect is removed. A 2 s content-match dedup window keyed on a{content, deadlineMs, committed}slot onManagedSessionprevents a double-send during a mixed-version rollout (legacy renderer + new server) — first matchingsendMessage(server timer OR legacy RPC) wins and claims the slot, subsequent matches within the window drop. Retry timer + pending slot are cancelled on both session-delete sites (main deleteSession path and the branch-creation rollback path). Fixes #804. Co-authored with Guillaume Gay. (5cb7b8c1) -
PR 378 review hardening (markdown / Electron URL handling, source-activation auto-retry guards, deterministic stale-credential cleanup) — Follow-up commit addressing review findings on the URL-safety, auto-retry, and credential-cleanup PRs above; also removes a leaking source-test module mock that was hiding the credential-cleanup regression. (
42b986e1)
Breaking Changes
- None. All changes are backward-compatible.
Assets 6
v0.9.5
v0.9.5 — Compact-mode UX polish and MCP / branching stability
Features
-
Compact-mode drawer for the session row menu — Session row context menus on compact / mobile layouts now open as a vaul drawer instead of a clipped Radix popover, matching the drawer pattern already used for status, labels, share, and messaging actions. (
a0468049) -
Drawer-based working-directory selector in compact mode — The working-directory picker switches to a drawer on narrow widths so the path list, recent dirs, and "Choose folder..." action remain reachable without horizontal scroll. The desktop dropdown is unchanged. (
6e147c03) -
Expandable chat input on compact layouts — Users can now tap to expand a collapsed chat input on compact / mobile views so longer prompts are easier to compose without leaving the active session. (
15fe589b) -
Drawer-based AcceptPlan picker in compact mode — The plan-acceptance UI uses a drawer in compact / mobile layouts so action rows aren't pushed off-screen on narrow widths. (
f8e92a11) -
Compact model selector in the web UI's mobile / auto-compact view — The web UI's model picker collapses into a compact selector when the shell width crosses the mobile breakpoint, matching the Electron app's container-query behavior. (
75c7825d)
Improvements
-
Shared
useWorkingDirectoryStatehook — Path resolution, recent dirs, and folder-picker handling are extracted into a single hook so the desktop dropdown and compact drawer can't drift in behavior. (aa01895d) -
Windows RTK install docs — Clarifies the Windows install path in the RTK setup docs so the "RTK not found" detection in Settings → AI → Performance matches what users see on Windows. (
129c28ff)
Bug Fixes
-
Branching on the latest turn no longer drops the last assistant message — The Pi-backed branching path now captures the conversation anchor after the SDK appends the new entry, so branching off the most recent turn no longer truncates the message that triggered the branch. Fixes #782. (
0988d0d6) -
Stdio MCP
source_testgives real diagnostics instead of a fake timeout —source_testfor stdio MCP servers now runs a single process with a proper stderr-activity watchdog and surfaces actual startup output, replacing the false "Server startup timeout" that fired for any slow-but-healthy server. Fixes #787. (a7fa0a3a,69d74167) -
Parallel
source_testcalls no longer wedge a session — When a source-activation abort interrupts a turn, siblingtool_resultblocks are now drained before the abort propagates so the SDK doesn't end up with orphanedtool_useIDs that permanently jam the next turn. Fixes #790. (58087fbf) -
AcceptPlan dropdown positioning and compact visibility — The desktop AcceptPlan dropdown is now built on Radix
DropdownMenu, fixing anchor drift and viewport clipping that could push actions off-screen. The web UI also now actually renders the dropdown in compact / mobile views — previously hidden behind a wider-only branch. (01d062e3,d32f390d) -
Chat view no longer stuck on "Thinking..." when a turn ends on a tool call — Mirrors the messaging-gateway fix on the Electron main UI side. When the Pi agent emits intermediate text plus a tool call and then completes without a non-intermediate
text_complete,groupMessagesByTurnnow uses the session'sisProcessing=falsesignal to mark the open turn complete and promote the intermediate text as the response. Pre-fix the chat sat on "Thinking..." forever and the user had to send a follow-up message to unstick it. -
Messaging gateway delivers the final assistant message in
progress/final_onlymodes — Automations whose last action is a tool call (e.g. the agent uses a tool to send the Telegram message and never emits a clean non-intermediatetext_complete) used to leaveprogressfrozen on💭 thinking...andfinal_onlysilent. The renderer now tracks the most recent assistant text regardless ofisIntermediateand falls back to it oncompletewhen no clean final arrived; genuinely empty runs still leave the status label in place. Thanks to @nheagy for the original PR (#779). (13187c2c) -
SDK Agent subagent activity groups are collapsible again — The activity grouping for SDK Agent subagent runs no longer renders flat; the collapse toggle behavior is restored. (
774e7ecb)
Breaking Changes
- None. All changes are backward-compatible.
Assets 6
v0.9.4
v0.9.4 — RTK token optimization, compact-session fixes, Codex transport stability
Features
- Optional RTK Bash token compression — Adds opt-in support for RTK in Settings → AI → Performance. Craft Agent still shows and permission-checks the original Bash command, but when RTK is installed (
>=0.23.0) and enabled, the execution path is rewritten throughrtkso common development-command output is compressed before it reaches the model. The settings UI now detects missing/outdated installs, offers Get RTK + Re-check actions, and shows saved-token / efficiency stats once RTK has processed commands. (754d254c,a96b8706,57452664)
Improvements
-
Backend packaging cleanup after Pi consolidation — Removes stale Copilot/Codex binary packaging entries, import guards, runtime fields, and docblocks that survived the move to the actual two-backend architecture (
ClaudeAgent+PiAgent). This trims dead build metadata and makes the codebase less misleading for future backend work. (a96b8706) -
OSS README metadata refresh — Adds the Trendshift badge to the OSS README so repository discovery metadata stays current. (
7feae925)
Bug Fixes
-
OpenAI/Codex long-running session instability — Upgrades the Pi SDK stack to
0.73.1, picking up the upstream Codex transport fixes: WebSocket setup can fall back to SSE before streaming starts, and cached WebSocket sessions are closed properly during session shutdown. This targets the1011keepalive timeout,1006disconnect, and certificate-verification failures reported for long-running ChatGPT Plus / Codex OAuth sessions. Fixes #747. (e30762b6) -
Compact session menu no longer clips nested actions — On narrow/compact layouts, the chat title menu now uses a vaul drawer with iOS-style drill-in panes for Status, Labels, Share, and Messaging instead of Radix nested dropdowns anchored inside the container-query panel. This keeps status and label actions reachable on small widths while leaving the desktop dropdown unchanged. The follow-up review fixes close leaked desktop dropdown state when switching layouts and prevent drawer actions from retargeting if navigation changes the active session while the drawer is open. (
db9f506f,084547e8) -
Rapid session-label toggles are race-safe — Extracts shared session-menu behavior into
useSessionMenuActionsand moves label toggling to optimistic state that compounds rapid taps, avoids Strict Mode double side effects, and resets correctly when switching sessions. This fixes compact drawer multi-label editing and removes behavior drift between desktop and compact session menus. (f0c12ed1,88f8d733) -
Skills "Show in Finder" opens the real skill folder — Skills list, skill detail, and skill menu actions now reveal the authoritative
skill.pathdirectory instead of constructing a syntheticSKILL.mdpath, and failures surface via a platform-aware Finder/Explorer toast instead of silently no-oping. Fixes #756. (d44e5691)
Breaking Changes
- None. All changes are backward-compatible.
Assets 6
v0.9.3
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. ...
Assets 6
v0.9.2
v0.9.2 — OAuth refresh ordering, Pi system prompt persistence, cross-machine spawn guard, i18n key restore, source_test/streaming/browser hotfixes
Bug Fixes
-
OAuth tokens silently refresh before agent build — On cold sessions,
SessionManager.sendMessagerangetOrCreateAgent→buildServersFromSources→refreshOAuthTokensIfNeededin that order. The first build saw stale tokens, emittedAUTH_REQUIRED, and the wrapper calledmarkSourceNeedsReauth— flippingisAuthenticated=falseon disk. The user saw a brief "needs auth" UI flicker before the late refresh restored state. Three coordinated changes: (1) refresh now runs beforegetOrCreateAgentso its internal cold-session build sees fresh tokens, the oldrefreshOAuthTokensIfNeeded(refresh + conditional rebuild) is gone, and a single post-refresh build is the only build per send; (2)buildServersFromSourcesreclassifiesAUTH_REQUIRED→TOKEN_EXPIREDwhen the credential is merely expired-but-refreshable and skipsmarkSourceNeedsReauthin that case (prevents flicker if refresh is skipped, e.g. during cooldown); (3)TokenRefreshManager.ensureFreshTokenfailure branches now mirrormarkSourceNeedsReauth's disk write tosource.configin memory, soisSourceUsable()returns false and the failed source is excluded fromintendedSlugsby the post-refresh build. Closes #710. (347820ab) -
Pi backend silently dropped the Craft system prompt — Pi SDK 0.72.1's
session.prompt()wipesagent.state.systemPromptback to_baseSystemPrompton every turn (agent-session.js~L796), sopi-agent-server's direct assignment was silently dropped — taking preferences/notes,<session_state>,<sources>, working directory, and all other Craft-built system-prompt content with it. The Anthropic backend was unaffected because it uses the SDK'sappend:option, which the Claude SDK appends verbatim. NewapplySystemPromptOverride()helper stamps all three SDK private fields (state.systemPrompt,_baseSystemPrompt,_rebuildSystemPrompt) so the prompt survives both per-turn resets and tool-change rebuilds. Pattern matches OpenClaw'sapplySystemPromptOverrideToSession— same SDK, same constraint, validated workaround until upstream exposes a public API. Wired into both call sites that previously didstate.systemPrompt = ...: the per-turnprompthandler and the ephemeralqueryLlmsession. Regression test asserts all three fields are stamped (catches the original single-field regression and any future drop of one of the writes). Closes #648. (13f6e63e) -
SDK spawn guard against stale branch cwd from cross-machine imports — The SDK's
Claude Code native binary not found at ...error is a misleading wrapper aroundspawn ENOENT, which fires whenever the subprocesscwdis also missing — not just when the binary is missing. Cross-machine session imports preservebranchInfo.sdkCwdfrom the source machine, so a Send to Workspace recipient hits this error on first chat even though the bundle is fine. A pre-spawn guard now catches stalebranchFromSdkCwdinchatImpl()and routes through the same branch-fallback recovery the post-failure path already uses (parent summary injection preserved). A newonBranchForkInvalidatedcallback persists all four fork fields atomically — fixing a pre-existing bug whereonSdkSessionIdClearedonly persistedsdkSessionId, leaving stale branch fields on disk to reload on next launch. ENOENT classification was lifted out of theif (isProcessError)gate (it never fired for the SDK wrapper, which is aReferenceError, not a process-exit error); now it disambiguatesbinaryvscwdvsunknowncauses, surfaces typedsdk_binary_missing/sdk_cwd_missingerrors after retry exhaustion, and keeps the 2-second auto-retry for the auto-update bundle-swap window. Helpers extracted toagent/spawn-helpers.tsfor unit testing (34 new tests; existing branching suite extended with v2 callback coverage). (12166f4a,e40a3801) -
source_testlastTestedAtnow persists — UI no longer always shows "Never" — Thesource_testhandler wrote an ISO string (new Date().toISOString()); the Zod validator invalidators.tsenforcesz.number().int().min(0), so the string was silently stripped before persist. A second hidden mismatch compounded it:session-tools-core/src/types.tsdeclaredlastTestedAt?: stringwhile the canonical shared type already usednumber. Fixed by switching the handler toDate.now()and aligning the local type tonumber. Storage, validator, and UI (formatRelativeTime) already expected a millisecond timestamp — no other files needed changing. Closes #708. (d4427cac) -
Assistant response content no longer disappears after completion —
handleTextCompleteunconditionally overwrote the streaming-accumulated message content withevent.textfrom the SDK completion event. Ifevent.textarrived empty (SDK race condition or intermediate event without text), the message bubble went blank. Now destructuresstreamingfrom state and applies a fallback chain in both the update-existing and create-new message paths:event.text || streaming?.content || existingMsg?.content || ''. Closes #709. (d4427cac) -
Browser toggle now fully disables system prompt section and prerequisite rule — Three independent sub-problems gated by the same
getBrowserToolEnabled()toggle that was already wired tosession-scoped-tools.tsbut nowhere else. (a) System prompt:system.tsunconditionally injected the## Browser Toolssection and its "calls are blocked until you read browser-tools.md" warning regardless of the setting — now gated. (b) Prerequisite matcher too broad:prerequisite-manager.tsusedisBrowserToolNameOrAlias()which matched any tool namedbrowser_tool, including external MCP tools likemcp__playwright__browser_tool— narrowed to session-scoped canonical names only ('browser_tool'and'mcp__session__browser_tool'). (c) Rule always registered: the prerequisite rule was registered even when the feature was disabled, causing external browser tools to be incorrectly blocked —getBrowserToolEnabled()check now makes the rule a no-op when the toggle is off. Closes #711. (d4427cac)
Improvements
- Spawn-guard defensive cleanup simplified per SOLID/KISS review — Rolled back over-engineering identified after the spawn-ENOENT fix while preserving the load-bearing bits (pre-spawn guard, atomic
onBranchForkInvalidated, working ENOENT classification,.app-bundle-aware regex). InlinedpickFirstExistingDirectoryandprobeEnoentPaths; deletedclassifyEnoentCause— the 4-line directory loop and 2-line probe didn't earn their abstraction at single call sites. Collapsed thebinary|cwd|unknowntrichotomy in the catch block to three plain steps: stale-cwd-with-branch → recovery, first attempt → 2 s retry, retry exhausted → typed error pointed at the more likely cause (removes 5 separateenoentCausebranches). Dropped the 5-valuereasonunion onrecoverFromStaleBranchForkto a singleisPreSpawnboolean — status text was the only differentiator. PromotedlastResolvedCwd/lastResolvedBinaryPathfrom instance fields tochatImpl-scoped locals (visible to the inner catch via closure) — avoids stale-state risk acrosschat()invocations.ErrorCodecentralized in@craft-agent/core/typesvia re-export fromerrors.ts. Net −206 lines, no behavior change; 608 agent tests + 102 server-core tests pass. (e40a3801)
Breaking Changes
- None. All fixes are backward-compatible.
Notes
- The OAuth refresh fix changes the order of operations on every send-message — refresh now runs once before any server build instead of after a stale build. If you operate API/MCP sources with custom OAuth flows, the visible behavior change is fewer disk writes per refresh cycle and no transient
needs_authflicker on cold sessions. No config or migration is required. - Cross-machine "Send to Workspace" recipients who previously hit Claude Code native binary not found at ... on first chat in an imported session will now succeed silently — the guard reroutes through the existing branch-fallback recovery and persists clean fork metadata for the next launch.
Assets 6
v0.9.1
v0.9.1 — Telegram bot whitelisting + access control, mid-stream send modes, scoped image-support toggle
Features
-
Telegram bot whitelisting + access control — Solves "anyone who finds the bot can talk to my Agent". Two-tier access model: (1) workspace owners at the platform level (
accessMode: 'open' | 'owner-only'), (2) per-binding access with'inherit' | 'allow-list' | 'open'modes. The first user who runs/pairin a paired supergroup is auto-seeded as workspace owner. Locking the bot to owner-only in Settings → Messaging → Telegram flips the workspace switch and migrates every legacy'open'binding to'inherit'so no public binding is left dangling. A new "Allowed users" collapsible (Usersicon) lists owners with remove controls; a "Pending requests" panel surfaces senders the gateway already rejected so the operator can Allow or Allow for this chat (per-row decision, never auto-promotes binding-level rejects to global owner). Inline buttons (bind:/perm:/plan:) run through the same access evaluator as text commands so a non-owner can't tap their way past the gate; bot senders are silent-dropped at the platform layer. The unlock path lives in the platform context menu (next to Reconfigure, only when locked). Composite-key pending-store (platform, userId, reason, bindingId) keeps a sender's workspace-level reject separate from their per-binding rejects so each can be resolved independently. Closes #672. (fd68c070,aa46043a,3f4fe418) -
Per-connection mid-stream send behavior (Steer vs Queue) — A new submenu in the Settings → AI connection context menu lets you choose what happens when you send a follow-up while the agent is mid-stream. Steer keeps today's behavior (redirect attempts to inject the new message into the live turn); Queue finishes the current turn cleanly and replays the queued message after
onProcessingStopped. The two modes have meaningfully different failure shapes — Claude's emulated steer can producesteer_undeliveredif no tool fires before the turn ends, paying tokens for nothing; Pi's.steer()is native and has no such failure mode. Defaults are picked per-provider at connection-create time (anthropic →queue, pi/pi_compat →steer) and stored inconnection.midStreamBehavior. 4 new i18n keys ×ばつ 7 locales. Closes the queue-vs-steer half of conversations around mid-stream UX. (35a4ca2e,f346035a) -
Per-model image support toggle in the chat-input picker — Custom-endpoint (
pi_compat) connections now expose a one-click image toggle next to each model in the chat-input model picker. When you stage an image but the active model resolves tosupportsImages !== true, an inline pre-flight banner appears above the attachment preview with an "Enable image support" action. Both surfaces resolve through shared helpers (modelSupportsImages/setModelSupportsImages) so they can't drift. The toggle now also renders in the single-model picker branch (used forpi_compatconnections with 0 or 1 models + adefaultModel). Storage schema unchanged — flipsconnection.models[i].supportsImages. Built-inanthropic/picatalogs stay SDK-owned and not editable from this surface. Closes #679. (f36c0833,bba2d726,31ddd5aa) -
Tool-result threshold scales with model context window — The fixed 15k-token threshold for large-response handling burned 25 % of context per tool result on a 64k-window model, and three or four moderate Read/Grep results could overflow even after Pi's auto-compaction. New
tokenLimitFor(contextWindow)helper: floor 2k, ceiling 15k, linearcontextWindow * 0.10between (64k → 6.4k, 128k → 12.8k). Pi-side tool wrapper sourcesagent.state.model.contextWindowper call so the threshold tracksset_modelmid-session. ClaudeAgent reads from the usage tracker. Addresses gap 1 from #666. (0d3c2796)
Improvements
-
Settings → Messaging i18n consolidation — Settings → Messaging mixed English fragments into every non-en locale (Hungarian showed "Reconfigure" / "Disconnect" / "Connect" alongside translated copy) because shared verbs were duplicated across per-platform namespaces and three keys resolved via
t(..., { defaultValue: ... })with no real entry. Added 7common.*action verbs (configure / connect / disable / disconnect / more / reconfigure / reconnect) translated to all 6 non-en locales, replaced 8 call sites inMessagingSettingsPage.tsx, deleted 9 redundant per-platform keys ×ばつ 7 locales (63 cells removed), and routed the hardcodedPLATFORM_API_DESCRIPTIONconstant through newapiTypekeys. Backfilled 8 platform-specific gap-fills that had been left as English. Net: 14–17 untranslated values per locale → 2–5 (only legitimate cognates like "Region" remain). Adding a new platform no longer requires duplicating verb translations. (bd575ed1) -
Locale-file sort guard — Across
en/de/es/hu/ja/pl/zh-Hans.json, 96 of 1393 keys per locale were out of alphabetical order — identical disorder confirmed past mass-translate scripts preserved insertion order. Newscripts/sort-locales.ts(run viabun run sort-locales, orbun run lint:i18n:sortedfor CI/check mode) normalizes in place. The pre-commit hook (scripts/lint-i18n-staged.sh) now blocks commits that stage unsorted locales with a "Fix: bun run sort-locales" hint, andvalidate:ciincludes the same check. Net change: zero keys added/removed; the locale-parity test suite goes 58 / 7 → 65 / 0. (3a40b5a5,3f4fe418) -
Pi SDK uplift to 0.72.1 — Bumps
@mariozechner/pi-ai,pi-agent-core, andpi-coding-agentfrom 0.70.2 to 0.72.1 across all fourpackage.jsonfiles. Drops the now-removedgoogle-gemini-cliandgoogle-antigravityentries fromPI_EXCLUDED_PROVIDERS. The 0.72.0 release replacedcompat.reasoningEffortMapwith model-levelthinkingLevelMap; verified Craft'sbuildCustomEndpointModelDefsets neither field so existing custom-endpoint registrations are unaffected. Adjacent fixes picked up: undici body/headers idle timeouts disabled for long streams (#3715), Anthropic stream-end-as-error handling (#3936), DeepSeek V4 reasoning compat, GPT-5.5 Codex support, websocket-cached transport for OpenAI Codex (#4083). (522a727f) -
HTTP MCP validation no longer spawns a subprocess —
validateMcpConnection()previously spawned a full Claude Code subprocess via the Agent SDK'squery()just to callmcpServerStatus(). On macOS the Electron sandbox killed that subprocess withSIGKILLbefore validation could finish, so every HTTP MCPsource_testfailed even when the server was healthy. Now usesCraftMcpClientdirectly (which already supports HTTP transport and runs alistTools()health check insideconnect()); drops the subprocess, the Anthropic env-var swapping, the Claude credential pre-check, and the deadclaudeApiKey/claudeOAuthToken/modelfields from the validation config. Net −231 / +119 lines. Fixes #697. (f0a12c4e) -
API source probe honors
testEndpoint.bodyand method; tool descriptions slimmed — POST-only API endpoints reliably 500'd from an empty probe body because the configuredtestEndpoint.bodyandtestEndpoint.headerswere ignored. The basic probe also now honorstestEndpoint.methodinstead of the HEAD→GET-on-405 dance that silently passed POST-only endpoints. Separately,guide.mdis no longer inlined into the API tool description on every LLM request — the agent'sPrerequisiteManageralready forces aReadofsources/{slug}/guide.mdbefore anyapi_{slug}call, so the guide always lands in conversation history once. For OpenAPI-imported sources this saves tens of KB per request and removes the bloat that pushed #683's payload past the relay limit. (589a51bf) -
IPC inventory script auto-updates the test snapshot —
scripts/ipc-inventory.tsnow rewrites the auto-generatedEXPECTED_CHANNELSblock inipc-channels.test.tsin place instead of just printing a migration report. Run it after adding/removing/renaming anyRPC_CHANNELSentry. (aa46043a)
Bug Fixes
-
Custom-endpoint
supportsImagestoggle now propagates to the live Pi subprocess — Toggling per-model image support wrote to disk but never reached the running Pi subprocess, so the model kept seeing attached images replaced by Pi SDK's "(image omitted: model does not support images)" placeholder. Two compounding issues:getOrCreateAgentgated the in-place runtime refresh onmanaged.isProcessing(whichsendMessagehad just flipped totrue, making the refresh branch dead code), and thellmConnections.SAVEhandler had no notification path to active sessions. Now SAVE fires the runtime push detached (so config persistence isn't gated onN ×ばつ 15sper-session timeouts), the gate usesagent.isProcessing()only, and a per-sessionagentRefreshLocksmutex serializes concurrent refreshes soagent.chat()can't fire against a still-applying update. Restart-required field changes (piAuthProvider,slug, auth/provider routing — fields theupdate_runtime_configIPC can't propagate) now route straight to dispose + recreate via a separatebuildRestartRequiredSignature. (81a6f195,6c0fec02,ebf01805,f0bbfe96) -
Vision toggle no longer blanks the trigger button on string-shaped models — Promoting a
models: ['gpt-4o']-shaped string entry to an object via the new image toggle dropped the model name (nonamefield), so the picker's trigger button rendered blank.setModelSupportsImagesnow seedsname: id, shortName: idwhen promoting; defensivem.name ?? stripPiPrefixForDisplay(m.id)fallback at the three picker display sites also r...