Repository navigation
cloud: v0.38.0
0.38.0 (2026-10-11)
Features
- ai: ask once per website and per chat instead of for every request (#861) (52fe800)
- ai: write Studio source as top-level text so providers cannot cut files to their first line (#940) (9557a82), closes #884
- assistant: make cld assistant a comfortable line-based agent with permissions and more slash commands (#938) (29b475d)
- cli: let plugins declare short module aliases and command shortcuts (#947) (75b2ce2)
- cloud: record every pending approval as one platform request with scope, binding and expiry (#943) (e5d874c)
- cloud: use the whole window on tablets and laptops, and show the way up to the pages above (#908) (677cf5d)
- dashboard: one board of widgets in three sizes that people can arrange themselves (#885) (0ff5872)
- mail: keep conversations that must not be deleted (#795) (5bf51dd)
- mail: keep phone links, let mailbox CSS win, and report what email removes from a message (#946) (82132c0)
- mail: re-run automations for messages whose earlier run failed (#941) (3a62d00), closes #914
- notebooks: one sidebar instead of the Navigator and simple modes (#916) (42df3f6)
- spaces: give ticking a task off hover feedback, a short animation, and Undo (#906) (6e85414)
- spaces: let claimed tasks move between Kanban columns and let any writer take a claim over (#835) (7875d2b)
- ui: float the workspace detail panel instead of docking it (#934) (cf25e0b)
- ui: keep drags from selecting page text on iPad (#902) (d65a827)
Bug Fixes
- ai: align the expanded work flush left and fold repeated steps into one row (#928) (ffa193b)
- ai: show the status text of a running turn quietly so it does not look like the answer (#933) (b0832f1)
- assistant: keep cld approval prompts consistent and safe in the terminal (#910) (064ba54)
- assistant: let one website approval cover a Studio app that reads with harmless headers (#904) (407696c)
- cloud: keep Cloud pages within the window when vh is taller (#880) (5fbe2dc)
- cloud: keep pop-out windows and public displays within the window when vh is taller (#948) (8670aa2), closes #881
- cloud: only follow the way up to a URL on this origin (#927) (8777efa)
- dev: give the development Postgres 1 GB of shared memory (#912) (159bfa8)
- mail: let a folder sync keep its turn on the provider lease between its batches (#942) (01164ea)
- mail: let incoming automations move messages instead of cancelling their commands (#899) (23ff84d), closes #876
- mail: never let a hand-edited signature break preview or sending (#903) (1049363)
- mail: open Focus views instantly and read them in about 100 ms in large mailboxes (#909) (2852300)
- mail: preset the Sent and Drafts folders so sender identities can be verified (#939) (64720c1), closes #878
- notebooks: keep the editor still when a checklist box is ticked with the mouse (#900) (cb3b91b)
- notebooks: render headings, tables, links and other smart markers in long notes once the editor has parsed them (#905) (b043383)
- spaces: reload only the Kanban columns a live change touches (#937) (3dc2886), closes #822
- ui: open hover previews only for the row still under the mouse (#883) (d9718f7)
Performance Improvements
- ui: split the message catalog per component family so small imports stay small (#936) (b8373dc), closes #841
Operator notes
TL;DR: No security advisory for released versions, but six changes need attention during the rollout. Core and Assistant migrate approvals (#861, #943): people can approve an Action for one chat and allow a website for one chat or one Studio app. web_extract and fetch_file now ask before they read an address the chat did not supply, and scheduled tasks that read such an address fail instead of reading it. Every approval of an Action or tool that Assistant asks for is now one platform request in the new approvals schema, used once and only for the review the person saw. Update Core first and Assistant right after. Rolling Core back below 0.38.0 needs a short SQL step first. The dashboard becomes one board (#885): deploy Core and the Dashboard together; each person's saved layout is converted once on their next visit. Mail can keep conversations (#795): update every Mail replica before people rely on Keep, and plan Mail's database storage. Incoming automations move mail again (#899, fixes #876): moves that waited for the mail server were cancelled; moves that were already cancelled are not repeated by the update, but a mailbox administrator can re-run them with a retry backfill (#941). Studio source writes change shape (#940): roll Core and Assistant over together, and check model profiles with a positive maxToolRounds. Mailbox CSS changes in two ways (#946): rules such as a { … } that were accepted but had no effect now style sent mail, and a saved design with a backslash or an image-loading function now stops that mailbox from sending until it is fixed. Check such designs before the update and the composer Preview after it. Signed-in pages now use the whole window from 768 px and show the way up to the pages above (#908), the detail panel of list pages floats over the content instead of taking a column of its own (#934), Notebooks has one sidebar instead of the Navigator and simple modes (#916), Mail's Focus views open at once and read in about 100 ms in large mailboxes (#909), and cld assistant becomes a line-based agent that offers local Bash behind an approval by default (#938), also reachable as cld ai (#947). There are no new environment variables, no production Compose changes and no new images. Install the new cld and run cld plugins update --all afterwards. If you skipped 0.37.0, its notes still apply: it fixed Help being readable without sign-in, migrated Assistant Skills one way in Core, and broke the Mail assignee API (see cloud-v0.37.0). Keep the gateway at 0.35.0 or later.
Reference pages at this tag: Update Assistant approvals for chats and websites, Allow a website for a chat, Read web pages with provenance, Record approvals as platform requests, Action approvals, Upgrade the dashboard to one board, The dashboard becomes one board of widgets in three sizes, Board and gallery, Mail storage for kept conversations, Keep conversations that must not be deleted, Effects, retry, and reconciliation, Spaces: implementation tasks and agent handoffs, Know the shell at each width, Show the way up, Add contextual detail, Update Studio source writes, Studio source writes save one file per call, Use Assistant from the terminal, Select a locale (CLI modules), Declare short names (CLI modules), Outgoing mail.
Before you deploy
- Back up the database right before the update. The schema changes can be rolled back with the steps under "Migrations", but the dashboard conversion empties each person's old layout settings, so only the backup brings those back.
- Update Core first, then Assistant and the Dashboard right after, all replicas at once (#861, #885, #940, #943). Core creates the
approvals.requeststable on its first start; until then, Studio code that calls an Action which asks for approval fails. Keep the gap between Core and Assistant short, because a mixed Core and Assistant rejects the other side'scode_writeinput as invalid, and an older Assistant has nocode_importroute; a turn running during the rollout may get one invalid-input error and then continues with the new tool. The new Dashboard asks Core for each widget in a size (widget=<key>@<size>); an older Core does not understand that, so widgets show as failed until Core is updated. - Update the other applications right after. The widgets of Accounts, Gateway Ops, IPA Hosts, Notebooks, Quotes, Spaces, Venue and Weather now come in sizes. An older application image shows its widget in the large size. A person whose saved layout is converted while an application restarts may lose that application's widget from their board (see "Migrations"), so keep this window short. Each application image also carries its part of the new shell (#908), so until all are updated, pages of older images keep the old header on tablets and laptops. The same holds for the floating detail panel (#934): older images keep the docked panel.
- Mail: replace all replicas at once (#795, #899, #941). An older replica still accepts deletes of kept conversations, still cancels waiting automation moves, and rejects a retry backfill as invalid input. Start retry backfills only after the rollout. Before the update, look for mailbox designs that the stricter CSS check rejects (#946, query under "Security"), because such a mailbox cannot preview, send or schedule mail until its design is fixed.
- Gateway: only the shared library changes; update it in any order.
- CLI: install the
cldbinary of this release, which follows the system language (#938), and runcld plugins update --allfor the new Assistant terminal agent and approval prompts (#938, #910), Mail (keep,unkeep,ls --view kept, theREASONcolumn ofidentity list,automation backfill start --retry-failed, formatting warnings on stderr) and Spaces (done --force). Runningcld plugins update --allwith the newcldalso saves the list of modules the Cloud serves and refreshes the plugin metadata, which activates short names such ascld aiforcld assistant(#947).
Migrations
- Assistant approvals (#861). Core's first start adds
conversation_idtoai.tool_approval_preferences, replaces the unique constraintai_tool_approval_preferences_uniquewith the indexai_tool_approval_preferences_reach, adds two columns toai.pending_actions, and createsai.web_addressesfor the addresses a chat's searches returned and its read pages link to. Assistant adds two columns toassistant.artifact_agent_approvals. All steps are idempotent. A chat approval and a chat's recorded addresses are deleted with the chat, an app's website approvals with the app.-
Rollback: before you start an older Core against this database, run in the Cloud database:
DELETE FROM ai.tool_approval_preferences WHERE conversation_id IS NOT NULL; ALTER TABLE ai.tool_approval_preferences ADD CONSTRAINT ai_tool_approval_preferences_unique UNIQUE (actor_user_id, tool_name, approval_scope);
An older Core expects the old constraint when it starts, and without the
DELETEit would apply a chat approval in every chat. People lose their chat approvals; approvals that apply everywhere stay. Roll Assistant back together with Core. Below 0.37.0 the 0.37.0 restore rule still applies. -
Check after the rollout: ask Assistant in a chat to run code that reads one public JSON URL twice. The first request asks and offers Allow this website for this chat; after you choose it, the second runs without asking and shows its full URL with Revoke.
-
- Approval requests (#943). Core's first start creates the schema
approvalswith the tableapprovals.requests, addsapproval_idtoai.pending_actionsandapproval_decided_viatoai.tool_calls, and gives every chat approval that is still open a request with the digests the running Assistant computes. Assistant addsapproval_idtoassistant.capability_calls. A Studio capability call that was waiting during the update gets its request when the person decides. Core deletes a request together with the capability execution records, 90 days after it expired. No configuration change.-
Rollback: an older Core and Assistant keep the new table and columns and ignore them; nothing needs undoing for this part. After the next update, Core gives the chat approvals that are open then a request.
-
Check after the rollout: approve one Action in a chat, then run in the Cloud database:
SELECT app_id, action, status, decided_via FROM approvals.requests ORDER BY created_at DESC LIMIT 1; SELECT action FROM audit.events WHERE target_type = 'approval_request' ORDER BY created_at DESC LIMIT 1;
The request shows
consumedandweb_session, and the eventauth.action.approve.
-
- Dashboard board (#885). The Dashboard's first start adds the nullable column
dashboard.user_settings.board. Each person's saved settings are converted once, on their next visit, into the board they saw: their order, a size per widget from its old zone and width, switched-off widgets left out, shortcuts kept. The old columnshidden_widgetsandwidget_layoutare emptied for that person, the name colour ingradientis no longer used, and the browser deletes the per-device cookiedashboard_widgets. People who never changed a widget follow the new default board. The conversion reads the widgets of the applications registered at that moment: a widget the saved settings name keeps its place while its application is stopped, but someone who only switched widgets off or changed a zone or width gets no widget from an application that is restarting during their first visit; they can add it again from the gallery.- Rollback: rolling the Dashboard back keeps the
boardcolumn but shows every widget to everyone whose settings were already converted, because their old settings are empty. - Check after the rollout: open the dashboard, select Edit, move a widget, select Done, and reload. The widget stays where you put it.
- Rollback: rolling the Dashboard back keeps the
- Mail keeps (#795). Mail's first start adds the table
mail.conversation_keepsand the triggercarry_conversation_keeponmail.conversation_messages, which carries a keep along when a message moves to another conversation. No configuration change.- Rollback: an older Mail leaves the table and trigger in place but ignores keeps, so it deletes kept conversations again when asked. The keeps apply again after the next update.
- No other application changes its schema in this release.
Security
- Assistant web reads need the chat's own addresses (#861). Until now
web_extractandfetch_fileread any address without asking, so a prompt injection in a mail or page could make the model put chat data into an address and send it to a website of its choice. They now read without asking only an address the chat supplied: the person's message, aweb_searchresult, or a link of a page read earlier, compared as whole URLs. Any other address shows an approval card with the full URL. Messages another chat sent do not count. A page someone else controls can still offer links to choose from, so the documentation advises reading only trusted sources in chats with data that must not leave them. - Website approvals stay narrow (#861, #904). A website approval covers one exact origin and only reads: GET or HEAD without a body, with at most
Accept,Accept-Language,Range,User-Agent,If-None-MatchandIf-Modified-Since, each up to 128 characters. Requests with secret references, a header a person's secret is bound to,Authorization, API keys, method overrides,Content-Typeor any other header still ask every time;CookieandProxy-*were never accepted. It is never offered as Always, and it applies only to turns a person started in a signed-in browser session. Scheduled tasks, mandates,cld, API keys and delegated accounts never use a remembered approval. Every request it lets through shows a receipt with its full URL and Revoke. The operator of an allowed website still sees the full addresses and those headers.web_extractreads through Firecrawl, which follows redirects on its side: Cloud returns no content from another origin, but the request has reached that origin by then, so a website with an open redirect can pass an address on. cld assistantshows model text safely (#910, #938). Before, streamed answers and Action reviews reached the terminal unescaped, so a prompt-injected model could print a fake review and hide the real one with terminal control sequences. Text output now escapes control and bidi characters (line breaks and tabs stay), and approval input in a review is bounded to 16,000 characters with a note when it is cut.--jsonand--jsonloutput is unchanged. Every terminal approval now asks in one shape: numbered choices (Approve once, Approve for this chat, Always approve) where the Action allows remembering, otherwiseApprove? [y/N]. Enter rejects, an unknown answer asks again, and typed-ahead or pasted input never answers. Update the Assistant plugin withcld plugins update --all.- Local Bash in
cld assistant(#938). The interactive agent now offers the locallocal_bashtool by default instead of only with--allow-bash. Each command still shows the exact command and start folder and asksRun? [y/N]first; a Bash approval is never remembered and never available non-interactively. An approved command runs as the person's OS user with their file access, and its command and output are stored in the chat and sent to the model. Bash starts without theCLD_*environment variables; other variables stay available.@pathattachments outside the start folder, with the home folder as start folder, or with secret-looking names such as.envask first, andcld's own configuration is always refused. People who do not want local Bash use--no-bashor switch it off as a profile default in/permissions; a stored default can only restrict. - An approval runs only what the person saw, once (#943, part of #897). Before, chat approvals and Studio capability calls lived in two app-specific stores: a chat approval could run up to 24 hours later even when the Action's live review had changed, nothing stopped one decision from being used twice, and asking again rewrote the pending snapshot in place, so a card still open in another tab could approve an effect the person never saw. Now every approval of an Action or tool that Assistant asks for, in a chat, in
cld assistantor from Studio code, is one request with the Action's scope taken from the capability ID, the requester, the approver, a level (standardorconfirm), the sentence and review the person saw, SHA-256 digests of the canonical input and the review, and an expiry of at most 24 hours for the decision. Local Bash confirmations incld assistantand HTTP requests that a Studio app asks for outside a chat keep their own approval and are not recorded as platform requests. The first decision wins and only the approver can decide. Right before the Action runs, Cloud recomputes both digests and moves the request fromapprovedtoconsumedexactly once. A chat Action whose review changed asks again instead of running, Studio answersCONFLICT, and an approval that was already used (for example after a restart during the Action) ends the call with an error instead of running it again. Every decision writes the audit eventauth.action.approveorauth.action.denywithdecidedVia(web_session,app_session,oauth,api_keyorinvocation). Routes, paths and responses are unchanged. The digests bind the input, not live state behind it, so an Action on changeable state stays protected only as far as its review shows; Mail'sdraft.sendcarriesexpectedRevisionfor this. - Outgoing mail HTML is stricter (#946). Hardening found in review: invisible signature marks (
⁣) can no longer split ajavascript:scheme so that it passes the link check, a comment inside a link address (te<!---->l:) now drops the address, and a line break inside a style value can no longer hide aurl()from the value check. Mailbox CSS no longer accepts values with functions that load an image (image-set(),cross-fade(),image(),element()with its-moz-form,src()) or with a backslash, which also covers CSS escapes such asu\72l(. Before, such values could make a recipient's mail client load a remote image. Mail checks the stored mailbox CSS on every render, so a mailbox whose saved design contains such a value, for example an escaped font name such as"\5FAE\8F6F\96C5\9ED1", can no longer preview, send or schedule mail: each message reports "The email design of this mailbox is no longer valid" until someone with Manage access fixes it with Edit design in Settings > Writing. Find candidates before the update withSELECT mailbox_id FROM mail.compose_styles WHERE strpos(custom_css, E'\\') > 0 OR custom_css ~* '(image-set|cross-fade|image|element|src)\s*\(';and open the composer Preview of each match after the update. - The way up stays on this origin (#927). The new way-up navigation of #908 could follow a tampered entry in the tab's session storage to another host. This is fixed before the feature ships; no released version is affected. A trail level that leads elsewhere, or to a
//path, stays a plain link.
Behavior changes
- The shell uses the whole window on tablets and laptops (#908). From 768 px, signed-in pages have no header: the app bar starts at 768 px instead of 1024 px and carries the profile avatar at its bottom. From 768 to 1535 px the app area meets the window's edges; from 1536 px it is a card again, still without the header. Phones and pages without sign-in keep their header. From 768 px regular pages scroll inside their content area, also on an iPad in portrait, where
AppWorkspacenow keeps panes side by side and opens the sidebar as an overlay from a compact bar. Instead of the header's breadcrumbs, each page shows the way up to the pages above: a title, a back link, or a path row whose middle collapses into a menu, with a back arrow on phones. Alt+Up goes up and returns to the parent page as the tab left it. Admin pages treat Admin as their root. Files goes up in place without stopping running uploads, and Accounts drops its own back buttons. - The detail panel floats (#934, closes #860). The detail of an item in Contacts, Capabilities, Files, Grids, Mail, Notebooks, Pulse, Spaces, Tools and Venue no longer takes a column of its own or replaces the list on narrow windows. From 768 px it floats 8 px inside the content area as a raised panel, and the list makes room while keeping at least 320 px; a remembered width stays between 320 and 640 px, and a double-click on the resize handle resets it. On touch tablets from 768 to 1023 px, a panel that can be closed opens as a drawer over the list and closes with a swipe to the right. On phones it is a sheet over the lower part of the window, half or full height, with a grabber and a pull down to close, and the tapped row scrolls above it. Escape closes the panel, J and K step through items, and F6 switches between the list and the panel. Boards, wide tables and week views keep their full width and continue under the panel. Notebooks can now close the panel in read mode too.
- iPad fixes (#880, #902, #948, closes #881). Cloud pages stay within the window on iPad Safari and Chrome, where the large viewport is taller than the window; a short page no longer scrolls by the toolbar difference, and Core's standalone Help page sizes the same way. The pop-out reference windows of Grids and Pulse, the Pulse public dashboard and the Weather display pages now size the same way. Drags no longer select page text, also when a sidebar handle is dragged with a mouse or trackpad on an iPad, and drags start only on a primary-button press.
- Assistant approvals (#861, #904). A rememberable Action offers Approve for this chat before Always approve. 17 Files, Grids, Mail and Spaces Actions can now be remembered; sending, deleting, sharing, inviting, task authority, Skills and outward commitments still ask every time. A Code Mode read offers Allow this website for this chat, a Studio app the person manages Allow this website for this app, and an app that sends
AcceptorUser-Agentwith its reads now asks once instead of on every open. The chat's Secrets & approvals dialog lists and revokes these approvals. Existing chats ask once before reading links from pages they read before the update, because those were not recorded. A scheduled task that reads an address it was not given now fails with a message instead of reading it; put the address into the task's prompt. - Approvals ask again when the review changed (#943). For people the only visible change: when an Action's review changes between the question and the run, for example because a draft got another recipient, the chat asks again with the current review instead of running the Action, and Studio code gets
CONFLICT. A decision in one place (chat,cld assistantor a Studio app) answers the same question everywhere, and a contradicting second decision from another tab gets the route's usual conflict answer, while repeating the same decision succeeds unchanged. Audit consumers see the new eventsauth.action.approveandauth.action.deny, whose target is the approval request labelled<appId>.<action>. - Assistant chat presentation (#933, #928). While a turn runs, the newest text turns into a quiet status as soon as a step starts after it, so a status such as "Now I write the app files" no longer reads like the answer; nothing moves when it changes, and finished turns are unchanged. The expanded work under "Worked N min" starts flush left, and consecutive calls of one tool that differ only in paging, such as reading one file in parts, fold into one row with a count.
- Studio source writes (#940, closes #884). Some OpenAI-compatible model backends cut source text nested in
code_write'sfilesarray to its first line, and Cloud saved the damaged file.code_writenow saves one file per call with top-levelpathandcontent, still guarded byexpectedRevision; imports withfromFilemove to the new reviewedcode_importtool, which keeps one review per batch. Every write result reportslinesnext tolength. Ordinary source writes still need no approval. Stored source, revisions and recorded chats are unchanged, and there is no migration. An app build now takes one tool round per source file: if a model profile sets a positivemaxToolRounds, check that it still covers a typical multi-file build, otherwise the turn stops using tools earlier. Check after the rollout: ask Assistant to build a small app withindex.htmlandapp.js; the turn shows onecode_writeper file without approval, with line counts that match the files. cld assistantas a line-based agent (#938). Approvals with more than one way to approve are numbered choices (once, for this chat, always), and Enter rejects. Ctrl+C cancels a question, or stops a running turn and keeps the session. A pasted block is one message,\continues a line, Tab completes commands and@paths, and@pathattaches a file. New slash commands:/new,/resume,/project,/retry,/web,/status,/usage,/permissionsand/approvals; unknown commands get a hint instead of reaching the model.cld assistant approvals list|revokemanages remembered approvals outside the session. Relative Cloud links in answers print as absolute links on the profile's server.cldnow follows the system language: without--localeorCLD_LOCALEit usesLC_ALL,LC_MESSAGESorLANG, and without a usable locale it sends noAccept-Language, so the installation's default locale applies. Before, it always sent English. Scripts that parsecld's human-readable output should set--localeor use--json. Server behavior is unchanged.- Dashboard (#885). One board per person with small, medium and large widgets on four columns (two on phones). Edit or a long press opens the edit mode on the board; Add widget opens a gallery grouped by app with live previews of the person's own data, loaded only for cards on screen and at most eight at a time. The focus, overview and side zones, widget widths, arrow ordering and the name colour are gone. A widget's
requiresRolesonly hides it from the gallery and the default board; its handler still authorizes every request. - Mail: Keep (#795). Anyone who can write in a mailbox can keep a conversation, as a hold against deletion. While kept, every path (web app, rules, workflows, API,
cld, capabilities, Assistant) refuses to delete its messages, move them to Trash, Junk or Drafts, or delete a folder that holds one, with409 CONVERSATION_KEPTorFOLDER_HAS_KEPT_CONVERSATIONS; a delete queued just before the keep is refused too. A message the mail server or another client deletes stays readable in Cloud, marked as deleted on the server; archive, move and mark act on that copy in Cloud only, and a server command for that one message reportsKEPT_COPY_ONLY. A message the server deletes before Mail loaded it, or one larger than 128 MB, has no Cloud copy. Only mailbox managers (Manage) can lift a keep; keep and release are audited asmail.conversation.keepandmail.conversation.keep.release. Lifting it lets the conversation be deleted again, and messages of which only Cloud's copy remains disappear from the mailbox; keeping it again does not bring them back. There is no retention date. Two further gaps: cancelling a scheduled message that is a conversation's only message still ends that conversation and its keep, and a folder another client deletes shows as missing with its messages but without the deleted-on-server mark. New surfaces:PUTandDELETE /api/mail/mailboxes/{mailboxId}/conversations/{conversationId}/keep, the Kept view (view=kept,cld mail ls --view kept), akeptsearch condition,cld mail keep|unkeep, and the capabilitiesconversation.keepandconversation.keep.release. - Mail storage (#795). Mail already stored every loaded message in Postgres and keeps it when the mail server deletes it, also without Keep, and it has no automatic cleanup and no size cap for message data. Plan Mail's share of the database at up to about twice the size of the loaded messages on the mail server, plan growth with the mail volume, and watch
SELECT pg_size_pretty(pg_total_relation_size('mail.message_part_chunks')), the largest share. Keep itself adds one small row and loads the conversation's missing messages. - Mail: incoming automations move again (#899, fixes #876). A
move_to_folderstep whose command waited longer than one 30-second recheck for the mail server was cancelled withWORKFLOW_CANCELED("The workflow run was canceled before command execution"), and the message stayed in the inbox. The step now keeps its command and moves the message. A command whose provider change may already have started becomesneeds_attentioninstead ofcancelled. Moves cancelled before the update are not repeated automatically: those messages stay where they are, often still marked as needing action, and a normal backfill skips them because their earlier run counts as already accepted. Re-run them with a retry backfill (#941, closes #914), see "Mail: retry failed automation runs" below. Known issue, not new in this release (#913): while a large backlog of moves drains, a folder sync can wait behind it for the mail server, so new mail can show up later than usual. #942 shortens this: a folder sync that needs several batches now keeps its turn on the mailbox's provider lease between them, so a move job that asks in the gap waits for the next sync batch instead of taking the mail server for a set of up to 50 moves. One observed delay before a sync's first batch is not explained yet, so #913 stays open. - Mail: retry failed automation runs (#941, closes #914). A mailbox administrator can re-run incoming automations for messages whose latest run of the current automation version ended
failedorcanceled, such as the moves lost to #876:cld --json mail automation backfill start <automation-id> --revision <revision> --retry-failed --yes(API fieldretryFailed), thencld --json mail automation backfill status <automation-id> <operation-id>. It never repeats runs that succeeded, need attention or are still running, and like a normal backfill it hands over at most 100 messages, so start another retry while the state islimited; afterwards the automation menu in Mail settings offers Retry failed messages. A retry runs the whole flow again: steps that set a state set it again, and Cloud refuses a retry for flows withadd_comment,create_reply_draftorcreate_space_eventsteps, because those would create their result twice. Like every backfill, it does not run flows with AI steps; those run only for new incoming mail. A run that moved its message before it failed is not retried. Runs from before an edit of the automation belong to an older version and are not picked up. To repair failed runs, use retry backfills alone: a normal backfill also re-runs messages the automation already processed on arrival. Automations > Activity shows how the new runs ended. - Mail sender identities can be verified right away (#939). A sender identity created through the web app, the API or
cldnow starts with the mailbox's Sent and Drafts folders. When an identity has no Sent folder at verification, Mail takes the mailbox's Sent folder and stores it only if verification succeeds; a folder that is already set is never replaced. An identity that cannot send says why:statusReasonissent_folder_missing,provider_rejected,verification_required, ornullonce verified. The sender settings, Compose, Reply and Forward show the reason, andcld mail identity listhas a newREASONcolumn (--jsonreturnsstatusReason). Nothing is migrated: an identity that stayed unverified before can now be verified again with Verify, which fills a missing Sent folder. - Mail: phone links, mailbox CSS and formatting warnings (#946, closes #870, #871, #873). A
tel:link in a Markdown mail, signature or template keeps its address when it holds a phone number (digits, an optional leading+, RFC 3966 separators and spaces, an optional;ext=); the compose safety review treats it like amailto:link. Platformmail.sendandmail.enqueueHTML keeps such links too. Mailbox CSS now wins over the built-in design: element rules such asa { color: #262626; }ordiv { … }were accepted before but had no effect, and they now change sent mail. Stored CSS that uses.mail-contentkeeps working. After the update, open the composer Preview of every mailbox with a custom email design. Creating or updating templates and drafts (web app, API,cld, draft capabilities) and the compose preview now returnformattingWarnings, at most 20 entries naming the elements, attributes such asclassorstyle, link addresses and Markdown images that email removes; the write still succeeds.cldprints them on stderr, and the composer shows them below the preview. Quotes and code blocks still lose their structure in sent mail (#932). - Mail: Focus is fast in large mailboxes (#909). With about 52,000 messages, switching a Focus view (For me, Unassigned, Waiting, All active) took about a second and a request up to 2.5 s, most of it spent by PostgreSQL compiling the queries with JIT. One grouped pass now computes the counts, a page stops after one page per mailbox, and both reads turn JIT off for their own transaction only; no database setting changes. The server renders the first page of every view, so a switch shows its rows at once; a view that has no finished read shows loading rows after 300 ms instead of a wrong empty state. The "All mailboxes except N hidden" hint is gone. The Focus API,
cld mail focus, views, counts and hidden-mailbox rules are unchanged, and the mailbox list views are not affected. - Notebooks: one sidebar (#916, closes #865). Notebooks drops the Navigator and the "Sidebar mode" setting. The notebook name on top switches notebooks, with the new-note button beside it and search below; Favorites, Recent (the 12 most recently changed notes) and a Tags menu turn the tree into a list, and the address keeps the filter (
?view=favorites,?view=recents,?view=tag&tag=...). Every row, also for readers, has one note menu with the favorite toggle, which also opens with right-click, the Context Menu key and Shift+F10. Nothing is migrated: the preference cookie keeps every other preference and drops the removed value on its next write, an old Navigator folder link opens the tree, and favorites stay on the server. Hover row actions no longer shift the row in Notebooks, Spaces and Contacts. cldshort names (#947, closes #891).cld ai …runscld assistant …. Plugins can declare a module alias and command shortcuts; core commands resolve first, then modules with exactly that name, including modules the Cloud serves that the profile has not installed, and short names last. A name that two modules claim, or that collides with a command or module, is inactive for every claimant, andcld helpandcld plugins list|install|updatewarn on stderr. Short names stay inactive until the profile has saved the list of served modules, whichcld loginandcld plugins list|install|updatedo.cld plugins listgains a SHORT NAMES column, and its JSON rows gainalias,shortcutsandinactive. Scripts should keep using full module names.- Spaces claims (#835). Claims coordinate work instead of locking it. A claimed task can move between open columns, and between done columns, again; before, every such move failed with
409. Anyone with write access to a Space, not only a Space admin, can now take over another person's or agent's claim, andrelease --force(task.releasewithforce) needs write instead of admin access. Completing or reopening someone else's claimed task, or moving it through a wormhole, asks once and then takes the claim over. Take-overs are recorded astask.taken_over; the previous holder's nextprogress,releaseordonecall gets409"Task claim is no longer active". A move withoutcompletednow follows the target column's done state. New:forceon the move and completion routes,task.set-completedandcld spaces done --force, and{ claimId, force }on the wormhole transfer route. Agents and scripts that relied on claims as locks should handle the new409. - Spaces task check-off (#906). A ticked task stays in place until the list rests, then collapses, and Undo restores its status, rank and completion. A move that completes or reopens a task now records
task.completedortask.reopened(orevent.*) and publishesitem.completed, like the completion endpoint, instead of a "moved" entry. - Spaces Kanban live updates (#937). A live change to one item first asks the server once, with one small probe per column, where the item is now, and then reloads only the columns that held or now hold it, instead of every loaded page of every column (6 to 12 requests per open tab for any change by anyone). A column that has more pages than are loaded, or hides items behind a filter, still reloads, and a change to several or unknown items still reloads the whole board. Completing, reopening, deleting or transferring a blocker now also announces each blocked task as updated.
- Smaller changes: a partly deleted Mail signature no longer breaks preview, saving, scheduling or sending, and render errors are now full English or German sentences while their codes stay the same (#903). Long notes render headings, tables, links and other markers across the whole note (#905), and ticking a checklist box with the mouse no longer jumps to the note's end (#900). Hover previews and Mail's quick look open only for the row still under the mouse (#883).
New settings
None. No new environment variables, no new images, and the JetStream reservation does not change. Keep, chat approvals, website approvals and the new shell need no configuration. The only Compose change (#912) gives the Postgres of the development stack (compose.yml) 1 GB of shared memory; compose.prod.yml is unchanged. cld takes its language from --locale, CLD_LOCALE or the system locale (#938).
Notes for app authors
@k2b/cloud 0.31.0 (depends on @k2b/ui 0.22.0; @k2b/ssr 0.15.0, @k2b/sync 7.0.0, @k2b/stdlib 0.28.0 and @k2b/nessi 0.17.0 are unchanged)
- Layout and the way up (#908, #927): the shell derives the way up from the
titlearray a page already passes toLayout, so pass the full path there and build no back buttons of your own. Applications that draw their own header placePageTrail. The new entry point@k2b/cloud/ssr/layout-runtimeexportslayout.update()andlayout.handleUp(); the destination passed tohandleUp()is always a path on this origin.AppWorkspacegets a compact mode from 768 to 1023 px without application changes. - Viewport sizing (#880, #908, #948): the shell measures the window with
dvhand is viewport-bound from 768 px. Surfaces rendered without the shell, such as pop-out windows and public displays, should useh-dvhormin-h-dvhinstead ofh-screenormin-h-screen, which follow the larger viewport on iPad. - Widgets (#885):
defineApp({ widgets })acceptstitle,description,sizes,defaultSize,suggestandrequiresRoles, translated underpresentation.translations.<locale>.widgets. Handlers read the requested size withgetWidgetRequest(c)from@k2b/cloud/server;streamWidgets()takessizes. A widget withoutsizeskeeps working as a large widget.presentation.defaultZoneanddefaultSpanare deprecated, but keep existing values while saved boards are converted.defineApp()now rejects a widget ID that contains@and a non-booleansuggestat startup. - Approvals (#861): a review of a rememberable Action may leave out
approvalScopefor arguments that should ask every time; before, it was required. Server tools can ask for a narrower target withcontext.requestApprovalFor(message, { toolName, approvalScope, always })and report requests let through on a website approval withcontext.reportWebsiteReceipts(). The namewebsite:readis reserved. - Approval requests (#943): Cloud runs an Action's review again right before the approved call and compares it with what the person saw, so reviews must be deterministic for the same input and state; a review that changes on every call (a timestamp, a random ID) makes the chat ask again each time. Give Actions on changeable state a revision in their input, as Mail's
draft.senddoes withexpectedRevision, and check it in the Action.@k2b/cloud/services/approvals(request,decide,consume) is platform-owned for Core and Assistant, not an application API; applications keep receiving approvals through their Capability Actions. - Chat presentation (#933, #928):
createAiChatTimelineshows the quiet status and the folded paging rows without changes in the consuming application. - CLI modules (#938): the command context gets an optional
ctx.openUrl(url)that opens an http(s) link likecld logindoes. It resolvesfalseover SSH or without a display, so always print the link too. - CLI short names (#947):
defineCliCommands()accepts onealiasfor the module andshortcutsthat map a name to an exact command path with its own handler, such asshortcuts: { stock: "items list" }. Names must match^[a-z][a-z0-9-]*$and differ from the module name and each other; a generated group, an unknown path, preset flags and preset arguments are rejected. A conflicting name is inactive for every claimant, so choose names that are unlikely to collide. - Studio source tools (#940):
CODE_SOURCE_TOOLSfrom@k2b/cloud/ainow includescode_import.code_writetakes{ id, expectedRevision, path, content, entry? }and no longer accepts afilesarray;code_importtakes{ id, expectedRevision, files: [{ path, fromFile }], entry? }. Sendcode_writecalls one at a time and pass each returned revision on, because calls in one response share anexpectedRevisionand all but the first returnCONFLICT. - Email HTML (#946):
EMAIL_HTML_ALLOWED_SCHEMESnow includestel, and HTML sent throughmail.sendormail.enqueuekeeps atel:address when it holds a phone number such astel:+49-731-000000; othertel:addresses are dropped. - Workflow effects (#899): a step that waits past its deadline runs again under a new execution generation with the same
ctx.effectKey. An action must adopt the work it already started under that key before other checks, and must not cancel it because of the new generation alone.
@k2b/ui 0.22.0 (no export is removed; peer dependencies unchanged)
- Smaller bundles, same imports (#936): each component family now carries its own message catalog, so an application bundles only the strings of the components it imports (a Button and DatePicker build drops from about 102 KB to 54 KB). Public exports, defaults and locale behavior are unchanged, so no import needs to change.
- Floating detail (#934):
AppWorkspace.Detailfloats insideAppWorkspace.Contentinstead of docking as a column, with the sheet, drawer and keyboard behavior described above, and no application change is needed for that.MainandDetailmust be direct children ofContent, possibly through a Solid island wrapper. New:AppWorkspace.DetailtakesonClose(Escape and the close gestures call it) andonStep(1for J,-1for K); a server-rendered panel marks its own close control withdata-workspace-detail-closeinstead. The application still ownsopenand the selection, and a page handler that uses Escape, J, K or F6 for local state callspreventDefault(). Opt-in trailing space: a board, wide table or week view that should keep its width and continue under the panel addsk2b-detail-trailto its horizontal scroll container (DataTablegainsscrollClassfor this), and fixed rows inMainsuch as toolbars, filters and pagination addvar(--k2b-detail-space, 0px)to their trailing padding. Without the opt-in,Mainreflows next to the panel. - New
PageTitlewith the page trail protocol and path collapse;AppWorkspace's head shows the trail and has the compact overlay sidebar (#908). - New
suppressTextSelection(pointerId)for custom drags; the built-in drags use it and start only on a primary-button press (#902).Widgetgains thefillsize, which takes its grid cell's height (#885). - Sidebar and menus (#916):
AppWorkspacesidebar tree items take an optionaldescriptionline, so a flat tree can show a filtered list with the same rows and keys. ANavigationaction item can setkeepOpen: true; it then runs withoutbeforeSelect, and the phone menu stays open to show the result, so use it only for actions that change what the menu shows, such as a filter or sort order. A keyboard-openedContextMenuappears beside the focused element instead of at the wrapper's corner.SidebarItemActionswithvisibility="hover"overlays the row's end instead of taking width from the label, so labels no longer cut off earlier and row metadata no longer moves on hover. Checkboxshows a faint check on hover, focus and press, animates when ticked without moving anything, and takes taps in a 44 px area on a coarse pointer when it has no label text; the box keeps its size (#906).HoverPreviewopens and swaps only for an anchor still under the mouse, also inside a shadow root (#883).ChartExplorer's sortable table header uses the same sort marker asDataTable(#928).
Also in this release
These commits are in the tag but missing from the changelog above, because release-please hides their types. None changes runtime behavior:
- #850, #862, #864, #868, #901, #920 test: steadier Mail view-count, Liquid template, GestureMenu, cost-alert recovery and Assistant finished-turn tests, and the Studio apps browser test expects the renamed Secrets & approvals action.
- #911 docs: the release and deployment pages no longer hard-code image and application counts and describe the release workflow chain that actually runs;
bun scripts/workspace.ts imageslists the release set. - #917 docs: contributors run focused checks locally and leave the full gate to CI.
- #930 ci: the Grids certification job runs Gotenberg at debug log level to diagnose a rare PDF/A conversion failure. Production is unaffected.