Tale v0.5.48
0.5.48 carries one merged pull request — every change since 0.5.47. 0.5.47 was tagged by the pipeline with generated notes on #3457 (the AI gateway drops its panel password and answers one token endpoint per vendor); nothing was tagged in between, and no number is skipped.
One theme in 0.5.48. A reply that used tools no longer shakes when its turn settles. On a long reply with tool steps — a weather lookup that reads a page four times, a knowledge search — the block above the answer that lists what the model did (Thought for Ns and its step rows) vanished for a third of a second the moment the turn ended and came back, so the whole answer jumped up by the block's height and back down; and a paragraph the model had written before its first tool call was held back until that same moment, then appeared above the streaming answer and remounted every block beneath it. Both were the same seam: the client's view of the reply held the streamed text across the gap between the stream's settle event and the refetched transcript row, but not the streamed parts, and read the settled rounds' text from a row that is refetched only at settle. Both are gone. A reply without tool steps never showed either, which is why a local check against a plain provider could not see it.
Highlights
A tool-using reply holds still when its turn settles (#3454)
Two settle-time jumps, measured in Google Chrome on a HiDPI display against platform.tale.dev with a reply that fetched four weather pages: the answer body moved up 142 px and back within about 300 ms (two layout shifts of 0.023 each), and roughly three hundred characters landed in one frame. The thread view kept a live row's text and reasoning through the gap between the stream's settled event and the transcript refetch, but its parts — the tool calls and results the thought timeline and the source cards draw — fell back to the placeholder row's empty list, so the timeline unmounted and the source cards with it; and the text of a round the model had already settled was read from that same placeholder row, which carries nothing until the finalized row lands, so the paragraph written before a tool call was invisible while the tool ran.
The reducer now holds the streamed parts the way it holds the text — set from the live channel (the newest of the streamed and held lists, served whenever it is longer than the row's own), and from a synthesized row's record so the handover to the real row keeps them; served through the settle gap while the placeholder's parts are shorter; cleared on finalize, where the finalized row's parts win on a tie as before — and reads the settled rounds' text and reasoning from those same parts for the live row and the synthesized row alike. What streams is byte-identical to the finalized text, so the reveal drains on without a re-render. Four reducer tests hold the live-row gap, the synthesized-row handover, the earlier-round text through settle and the synthesized row's text composition; each pair fails on the reducer without its commit. CHAT-F41 carries the manual check.
Behaviour changes
- Chat, a reply with tool steps: text the model wrote before a tool call is on screen while that tool runs, in its authored place above the later rounds, instead of appearing when the turn settles; the
Thought for Nsheader, its step rows and the source cards under the answer stay mounted through the turn's end; the answer body does not move when the stream settles, and the reveal keeps draining from where it was. A reply without tool steps is unchanged. The stale-live guard — the finalize race in which the channel still carries a prefix of text that already settled onto the row — keys off the same parts, so nothing is shown twice. - Nothing else changes: no user-visible string, catalog, setting, route, scheduled job, audit action or error code moves in this range; the platform's messages are untouched in every language.
API contract changes
- None in this range. The contract stays at 1.20.0: 86 paths, 135 operations, 63 schemas, 167
Error.codevalues; the shippedopenapi.jsonis byte-identical to 0.5.47's (and to 0.5.46's), andX-Tale-Api-Versionanswers1.20.0. - MCP: unchanged.
- The thread stream's SSE protocol (
GET /chat/threads/:id/stream:idle,progress,settled,heartbeat) is unchanged on the wire; the change is in how the browser holds what it already received.
Security
- No new surface. The change is client-side state handling over data the browser already received on the thread stream; it adds no route, permission, secret or storage. No dependency changes: the lockfile is byte-identical to 0.5.47's, and no
package.jsonmoves. - The dependency-audit and image-configuration lane, the SAST gate and the platform image scan run on the release commit as on every push to
main.
Known issues
- The fix is proved by reducer tests and by a real-browser A/B against a development stack, not yet against the platform fleet. The measurement that found the jumps was taken on platform.tale.dev running 0.5.46; the same measurement after this release is deployed is the closing check, and these notes will be corrected if it disagrees.
- A cosmetic flicker in the chat list stays: while a reply streams, the active conversation's relative-time label widens (
5s,20s,1m) and a long title beside it is re-truncated at each tick. It is the sidebar, not the transcript, and it is the same in every version. - 0.5.47 shipped with generated notes. Its one change, #3457, has no curated record: the AI gateway's panel no longer asks for a password (
AI_GATEWAY_PANEL_PASSWORDandAI_GATEWAY_SESSION_SECRETare no longer read; a deployment that still sets them boots fine, and access to the panel is whatever fronts the origin), andGET /api/tokensis replaced byGET /api/tokens/anthropicandGET /api/tokens/openai, each answering{ "tokens": [ … ] }in the retired cc-gateway's field shape (idis a string). A caller of the old single endpoint must move to the vendor path. - Unchanged from v0.5.46, where each is described in full: the Entra app-role fix is not verified against a live Entra tenant and a rule whose Matches value is a role id no longer matches; the AI gateway is deployed on its own and documented in its README only, with no inference proxy, account rotation, multi-user panel or usage history; an Own Compose or own Kubernetes deployment keeps the egress probe it declares; ui.tale.dev reports nothing until its deployment sets the three collector variables.
- Unchanged from v0.5.45, where each is described in full: a native OIDC client registered before 0.5.45 is refused by the CLI until its application type is backfilled with the one-statement
UPDATEthose notes give; ui.tale.dev ships one English tree; the oldoauthClient.typeandpubliccolumns stay;team.memberCountis a constant 0; the 0.5.42–0.5.44 window has no curated known-issues record, and the Settings > Governance > Vision model description (English, German and French) still says a model that already reads images never uses the vision model, which #3436 made inaccurate. - Unchanged from v0.5.41, where each is described in full: spend history booked before that release is not rewritten and a month-to-date total spans two pricing rules; off-peak and long-context pricing tiers are not modelled; cache prices are not surfaced; a pin to
deepseek-v4-flashanswersCHAT_MODEL_UNKNOWN; the Moonshot, Vercel and OpenRouter harness doors are declared from vendor documentation; the three lane-fix follow-ups (the Read hook's PDF guard, the resumed-retry replay, Z.ai's unused Anthropic door) are open; a shipped provider whose catalog carries no curated embedding entry is refused as an embedding provider; the custom-provider form's authoring limits and a bare listing's assumptions;tale-vision --thinking disabledis validated against a controlled upstream only; the chat scroll roundsCHAT-F39andCHAT-F40are browser-tested for the wheel and the follow latch only; thebun devruntime-image step was not observed live. - Unchanged from v0.5.39, where each is described in full: a knowledge entry the REST door wrote before that release keeps
source: "manual"; a scan reuses a robots verdict up to a minute old and a row stored earlier carries no sitemap list; the ask retraction is best-effort and forward-only; the app's own archive stays unfenced;GET /api/v1/teamsis read-only and a complete set; the chatcontentcap counts UTF-16 code units; thenullableon aoneOfbranch is the OAS 3.0 spelling; 0.5.38 shipped with generated notes and its four pull requests (#3424–#3427) have no known-issues record. - Unchanged from v0.5.37, where each is described in full: ledger rows booked before that release keep the subject they were booked under and a project agent can appear twice in Top assistants across the upgrade;
app.usage_eventsis write-retired, not dropped; nothing in the schema forbids a door string inusage_ledger.user_id; the run list labels a keyed startStarted by api-key:…; theGOV-F20round is manual;llmnodes are unmetered and a run carries no usage or cost. - Unchanged from v0.5.36, where each is described in full: automation
files:mounts and workflowdocument.*steps do not apply the team audience; a single-sign-on sign-in with an empty group list revokes nothing and a SCIM group replace overwrites hand-added members silently; the legacy team mirror columns stay; the three GIN indexes of migration 0109 were built withoutCONCURRENTLY; the team roundsNAV-F6,SET-F18,SET-F19,SET-F42,KNOW-F20,PROJ-F23,PROJ-F24andCONV-F12are manual; a team skill'steamslist is validated only when it changes; RESTDocument.teamIdstays as the deprecated single-team spelling. - Unchanged from v0.5.35, where each is described in full: a frame carries the signed-in session only from a same-site host page and the shell's embedding policy is the union across organizations; revoking a trusted-header key or turning the card off ends no session; the
AUTH-F21–AUTH-F24,AUTH-B10andSET-F41rounds are manual; approvals have no REST twin; moving a folder has no door and documents already at the root stay there; the auto-retry resumes only a turn that announced its conversation handle; the Google Drive row counts a deployment app from either lane. - Unchanged from v0.5.34, where each is described in full: a managed deployment gets the organization-creator behaviour only once its specification declares
organizations.creatorsand a new bundle is applied; theAUTH-B9andAUTH-F20rounds are manual; the creator list is matched against sign-in addresses. - Unchanged from v0.5.33, where each is described in full: the sign-up gate's first-boot race; the boot catch-up that marks provisioned accounts verified asks nobody; the break-glass administrator's password-rotation, single-sign-on-link and memory-adapter limits; the cross-scope webhook guard governs deliveries from that release on; a site's robots policy upgrades at its next scan; a scan waiting on render capacity takes longer by design; the governance pickers list only providers with an active credential.
- Unchanged from v0.5.32, where each is described in full: the embedding pacing is proved against a controlled server, its bound is per Tale process, and
minTokensPerSecondis a statement nothing verifies; the Kubernetes page's verified scope is one kind cluster,config-dataneeds RWX or a single node, and Tale ships no Helm chart. - Unchanged from v0.5.31, where each is described in full: a managed deployment picks up that release's proxy policy only when a newly prepared bundle is applied; the transcription setting is only as good as the organization's credentials; the six agent-turn fixes are bounded by the pinned Claude Code build they were read from; the 0.5.29 proxy change has been exercised live in
TLS_MODE=letsencryptonly; the web tier's backend-URL default lives in the image, not in the generated compose; the scheduled-pack fix does not reach an automation an organization already has; a budget hold covers a turn's first round only; nothing backfills a task timeline. - Unchanged from v0.5.20, where each is described in full: the
es/co-ccColombian cédula detector still ships switched off and a locale-agnostic PII toggle still widens national-ID matching to every locale; thinking-block replay on the native Anthropic connector is not done and the live Max-plus-tool-call check is still owed;rag_searchembedding calls inside a harness turn are unmetered; the product edit dialog cannot clear a field; the app's skill editor still carries the retiredprivatevisibility. - Cloud sync, left for later: there is still no Sync now action — the cadence is the fifteen-minute scan, so a reconnected account waits for the next run. A config whose owner leaves the organization is still deactivated silently by a different door, and a source-deleted item is still a status stamp with no bell.
- Documents indexed before 0.5.27 keep one vector per repeated passage until they are re-indexed; the content hash is unchanged, so only an explicit
retry-indexing(or a content change) re-embeds them. - The rail's navigation memory has had part of its manual round: the R5 round drove six EN/DE/FR desktop and phone cases covering parts of
NAV-F16–NAV-F19; the remaining section, the second-account cases andNAV-B6–NAV-B9are still unrun. - A reply-language directive is a directive: a model may still answer in the prompt's language and nothing on the wire marks a slip.
- No image input on the REST chat send. A
visionmodel reads an image over REST only on a thread the app continued with an image attachment; the design of anattachmentsfield on the send is recorded as contract debt. - No REST door authors or deploys an automation —
POST /automationsanswers 405 by design. Build and deploy in the app, or over the MCP endpoint'ssave_automationanddeploy_automation; the REST key lists, reads, runs, answers asks and wires triggers. - The
x-tale-paginationextension is a declaration on the OpenAPI document; generated clients that do not read vendor extensions still branch on the two cursor names untilcursoris retired. - The app's zip upload of a skill bundle rewrites the bundle and moves
updatedAteven when the zip is byte-identical, wherePUT /skills/{slug}writes nothing. - A tool call the reply cap cut keeps
input: {}on the storedtool-callpart; the raw text the model emitted is still not on the transcript. - Folder names written before 0.5.24 keep their bytes; a sync engine's hub-path lookup can create an NFC twin beside a legacy NFD folder. No backfill ships.
- Two bounded document readers still filter after their cut; both report an honest
truncated, so a caller can tell the answer was cut. - Behind a Docker-published port, every IPv6 client arrives as the bridge gateway's address and shares one per-address rate-limit bucket and one audit address until the daemon runs with
ip6tablesand the reverse proxy's network is IPv6-enabled — an operator item, documented on the Own Compose page. - Recorded as contract debt, each with its design in the ledger: a queued send is invisible on the message list until a worker opens it; a webhook delivery the deployed
inputsschema refuses moves no trigger stamp; the MCPrun_deployedtool keys its idempotency apart fromstart_runand REST; a page is fetched three to four times per scan; a cancelled run answerstrace: nullandeffects: nullwhere a failed run answers both; approvals have no REST twin; a task can be archived and restored over REST since 0.5.43 but still not deleted; a webhook bind does not say whether the deployedinputsschema admits a delivery; an exhaustedrepeatUntilis only a trace note;Websitecarries noscanStartedAtand the crawler has no page cap, path filter or stop verb of the caller's; website search has no dense leg and its substring fallback stampsscore: 0; noIdempotency-Keyon the task start; no queue position on a queued send; a corrupt Office document still fails asindexer_errorand is retried five times where a PDF landsmalformed; no/.well-known/security.txt; no changelog feed on tale.dev; no SDK, collection or per-code table beyond theError.codeenum;GET /notificationsrows carrytypeas a free string and nothing pushes them to a machine caller; a skill keeps no version history on the machine door; the per-task circuit breaker is not built; the messages a conversation snapshot applied are readable only in the app; a run carries no usage or cost.
Migration notes
- No migration. The application database stays at 0112; the knowledge database is unchanged;
db/migrate.tsandauth/auth.tsare untouched, so Better Auth stays at 1.7.5 and its migrator has nothing to add — the boot runs the same idempotentteam.memberCountdefault 0.5.45 introduced and nothing else. - Rolling-deploy safe. No schema, contract, catalog or message change; the previous and the new platform image read and write the same tables, and a browser that loaded the 0.5.47 bundle keeps working against a 0.5.48 backend (and the other way round) because nothing on the wire moves.
- No platform environment variable is added or removed; the root
.env.exampleis unchanged. No scheduled job, audit action, error code, catalog, provider definition or platform message key changes. compose.ymlis unchanged, and so areservices/proxyandservices/db: no image in the stop-gated tier changes — a plaintale deployis the whole upgrade, no--stop, no downtime window.- Images: only the platform image carries a source change (the thread-view reducer in the browser bundle). The other eleven —
docs,ui-docs,web,proxy,db,ai-gateway,sandbox,sandbox-egress,sandbox-runtime,sandbox-buildkitd,sandbox-llm-gateway— are rebuilt at the tag as every release rebuilds them, from sources and a lockfile identical to 0.5.47's. - The CLI does not change in this range:
tools/cli,packages/shared, the embedded configuration catalog andcompose.ymlare all untouched since 0.5.47, so the release executables are rebuilt at the tag and report 0.5.48 but behave as 0.5.47's. A managed deployment may move its runtime pin to this release under a 0.5.47 CLI; moving both together stays the documented practice. @tale/uiand@tale/marketing-uiare pinned by this release as theui-v0.5.48andmarketing-ui-v0.5.48tags on their snapshot branches; a consumer outside the monorepo installs"@tale/ui": "github:tale-project/tale#ui-v0.5.48". Neither package changes in this range, so both tags are content-identical to their 0.5.47 predecessors.
Upgrading
-
On the 0.5 line (0.5.0 – 0.5.47; there is no 0.5.42 deployment to be on):
tale update tale deploy
Nothing in this release needs
--stop. A deployment crossing 0.5.44 also takes Better Auth 1.7's tables and columns and thememberCountdefault at boot — and, if it declares native OIDC clients created by an older CLI, needs the one-statement backfill in the 0.5.45 notes; one crossing 0.5.45 takes the egress probe that 0.5.46 introduced (the egress container is recreated by the deploy); one crossing 0.5.41 runs migration 0112 at boot as well; one crossing 0.5.39 runs migration 0111, one crossing 0.5.38 runs that release's0108_approvals_one_pending_conversation_draft.sql, one crossing 0.5.37 runs migration 0110, one crossing 0.5.36 runs migration 0109, one crossing 0.5.35 runs migration 0108 (0108_trusted_header_keys.sql) and Better Auth's session column, and one crossing 0.5.33 runs migration 0107. A deployment crossing from a version older than 0.5.29 should read that release's notes, which do: itsproxyimage change is only applied by a--stopdeploy. -
Nothing to re-point before you upgrade: no catalog, model, environment variable, setting or contract field changes in this range. A browser tab that is open on a conversation picks the new bundle up at its next load; a reply already streaming in an old tab keeps the old behaviour until then.
-
Managed deployments move by pinning the CLI and the runtime to this release's commit, preparing a new bundle and applying it with the pinned CLI — see Managed deployments on the CLI install page. The bundle's backend-local phases run under the interpreted CLI (
cli/tale.mjs) that thesetup-cliaction andbun run --filter @tale/cli buildproduce beside the executable; the executable from the release page has no interpreted bundle beside it and cannot prepare a managed bundle. On a Linux x64 host whose CPU lacks AVX2, passlinux-baseline: 'true'to thesetup-cliaction so the bundle embeds the baseline executable. -
The AI gateway is deployed on its own, not by
tale deploy; its image is unchanged since 0.5.47 apart from the version label:cp services/ai-gateway/.env.example services/ai-gateway/.env # fill in the two secrets VERSION=0.5.48 docker compose -f compose.ai-gateway.yml pull VERSION=0.5.48 docker compose -f compose.ai-gateway.yml up -dThe pool lives in the
ai-gateway-datavolume; back it up, because losing it loses every authorized account, and keepAI_GATEWAY_ENCRYPTION_KEYwith it. The panel answers on port 3004 and, since 0.5.47, asks for no password of its own. -
New install:
curl -fsSL https://raw.githubusercontent.com/tale-project/tale/main/scripts/install-cli.sh | bash mkdir tale-05 && cd tale-05 tale init tale deploy
On a CPU without AVX2 the downloaded executable aborts with
Illegal instruction; build it from source withbun run build:linux-baselineintools/cli.
What's Changed
Full Changelog: v0.5.47...v0.5.48