Repository navigation
Releases: columenlabs/lumen
Releases · columenlabs/lumen
Release list
v0.1.137
Key takeaways
- [Fix] Bound web link Test now shows its result: In
Assistant.status()from @columenlabs/lumen-ai, an app that reaches its web-fetch service over a service binding now gets thetestfield on the web link's status, so the Settings ▸ Assistant ▸ Web badge shows the last result instead of staying on "Not tested". - [Improvement] Bound link tests are saved:
POST /web/teston a bound link now stores its result in its own app-scope doc, so a Test you run is still there after you reload Settings. - [Improvement] Read the stored bound-link result from state: The state object has a new reader for the bound link's saved doc, so custom code can read the last test without calling
Assistant.status().
Shell
- The Web section of Settings ▸ Assistant shows the saved test result for a bound link. The badge no longer stays on "Not tested" after a successful Test.
- There is no UI change. The block already draws the test state from
wayTest(state), and it now receives data in the bound case.
AI
POST /web/teston a bound link saves its result in a dedicated app-scope doc. A bound link has no token doc to keep the result beside.Assistant.status()reads that doc and returns thetestfield for the bound case. Before, it returned only{ set: true, bound: true }.- The state object gains a reader for the bound link doc.
- The token path is unchanged.
- There is no new key or table. An older build ignores the new doc, so there is nothing to migrate and no change to make in your app.
Pull requests (1)
Full changelog: v0.1.136...v0.1.137
v0.1.136
Key takeaways
- [Feature] The assistant now knows the current time and when the chat last spoke: In
@columenlabs/lumen-ai/assistant,createAssistantnow puts a "Bearings" block (the server's current time in the user's zone and in UTC, plus how long ago the last message in the chat was) at the top of every user message, so the model no longer guesses the date. - [Feature] Add your own app lines to the bearings:
createAssistantaccepts a newappname and abearings: (b, caller) => linesoption, and any lines your hook returns appear under the app name; a hook that takes longer than 300 ms or throws is skipped so a turn is never held up. - [Feature] Build bearings yourself:
@columenlabs/lumen-ai/assistantnow exportsbearings(),bearingTime,bearingSpanandknownZonefor formatting the current time, elapsed spans and validating IANA zone names in your own code. - [Improvement] The browser's time zone travels with each message:
useAssistantChatfrom@columenlabs/lumen-uinow sends the browser's IANA zone with every message, using the new optionaltime_zonefield onAssistantMessagefrom@columenlabs/lumen-ui-types/assistant, which is backward compatible. - [Improvement] The system prompt no longer carries a UTC date: The assistant's system rule now tells the model to take today and now from the bearings, and because the bearings sit in the user message rather than the system prompt, the prompt cache still holds.
Layout and widgets
useAssistantChatin@columenlabs/lumen-uinow includes the browser's IANA time zone in each message it sends, with no change needed in your app.
Types
AssistantMessagein@columenlabs/lumen-ui-types/assistantgains an optionaltime_zonefield. Existing code keeps working.
AI
- Every user message now starts with a "Bearings" block: the server's clock as now (local time with zone, plus UTC), how long ago the last message in the chat was, the app name, and any lines your app adds.
- The block is built from the server's clock, and the model is told to use it for today, now and how long ago, never to guess the date.
createAssistanttakes two new options:app(the name shown in the block) andbearings(a hook that receives the bearings and the caller and returns extra lines).- Each app hook has 300 ms to answer. If it is late or throws, only its lines are left out and the turn goes ahead.
- The block sits at the top of the user message instead of the system prompt, so the cached system prompt stays stable. The old UTC "Today is" date is gone from the system rule.
- New exports from
@columenlabs/lumen-ai/assistant:bearings(),bearingTime,bearingSpanandknownZone.
There are no breaking changes in this release.
Pull requests (1)
Full changelog: v0.1.135...v0.1.136
v0.1.135
Key takeaways
- [Feature] Reach scrapeql web tools over a service binding: In
createAssistantfrom@columenlabs/lumen-ai, theweb.scrapeqloption now takes an optionalfetch(the fetch of a service binding), so the assistant calls the/mcpendpoint through the binding and needs no service token. - [Improvement] Assistant settings show a bound link: In the
@columenlabs/lumen-uiassistant settings, a boundscrapeqlnow appears as aLumenConnectionwith one read-only "App link" way and a Test button for admins, in place of the token form. - [Improvement] Status says when the link is bound: In
@columenlabs/lumen-ui-types,AssistantStatus.web.scrapeqlgains an optionalboundboolean, so a client can tell a binding from a stored token.
AI
createAssistant'sweb.scrapeqlaccepts an optionalfetch. When it is set, the assistant reaches the/mcpendpoint through that binding.- With
scrapeql.fetchset, the scrapeql tools always join the assistant. No stored token is read. scrapeqlTools(url, token | null, fetch)sends token headers only when a token is given.- The engine's
testScrapeqlnow tests the link, and a newboundScrapeqlgetter tells you whether the link is bound. - The assistant status reports
web.scrapeql = { set: true, bound: true }for a bound link. - Routes:
PUT /web/scrapeqlanswers 409bound, since no token is needed.POST /web/testtests the link.DELETE /web/scrapeqlstill removes an old stored token.
- An app that does not pass
fetchbehaves exactly as before. This is not a breaking change.
Layout and widgets
- In the assistant settings, a bound
scrapeqlis shown as aLumenConnectionwith one read-only "App link" way. Admins get a Test action, and the token form is hidden.
Types
AssistantStatus.web.scrapeqlin@columenlabs/lumen-ui-typesgainsbound?: boolean.
Docs
- The
@columenlabs/lumen-aiREADME documents the newfetchoption onweb.scrapeql.
Pull requests (1)
Full changelog: v0.1.134...v0.1.135
v0.1.134
Key takeaways
- [Fix] Long Settings section lists now scroll: In
LumenSettingsfrom@columenlabs/lumen-ui, the desktop dialog's left section list scrolls on its own when an app has more sections than fit the dialog height, so every section can be reached. - [Fix] Search stays pinned above the list: In
LumenSettings, the nav search no longer scrolls away, because only the groups live in the new scroll area (data-slot="settings-nav-list") that you can target for styling. - [Improvement] Deep-linked sections scroll into view: When
LumenSettingsopens on a section far down the list, for example through the assistant'ssettingsmove or a?settings=<id>link, that row is now scrolled into view.
Shell
LumenSettingson desktop: the section list is now its own scroll area, with the search field pinned above it.- The scroll area contains its scrolling (
overscroll-contain), so reaching the end of the list does not scroll the page behind the dialog. - The selected section is scrolled into view when Settings opens on it.
- Phone is unchanged: it still uses the
LumenSelectorpicker. - Not breaking. No prop changes, and existing apps get the fix without edits.
Docs
- New story
App shell/LumenShell ▸ Long Settingswith 7 groups of 4 sections each, for checking how Settings behaves when the section list is taller than the dialog.
Pull requests (1)
Full changelog: v0.1.133...v0.1.134
v0.1.133
Key takeaways
- [Fix]
savedAnswersno longer crashes on import when the browser blocks storage: In@columenlabs/lumen-ui,savedAnswersnow readsindexedDBinside a try, so a browser that throws aSecurityErroronwindow.indexedDBno longer stops your app from drawing. - [Improvement] Missing storage falls back to the server: When
indexedDBis unavailable,savedAnswersswitches to its no-op persister, so every load asks the server and your app still gets its data. - [Fix] A throwing
openis retried later: IfindexedDB.openthrows synchronously,savedAnswersnow rejects the promise instead of throwing, so the store is tried again on a later call.
Pickers and forms
savedAnswersno longer throws when the browser refuses storage, including when merely readingindexedDBraises an error. This used to fail at module load and leave the app blank.- With no usable storage,
savedAnswersuses the no-op persister. Nothing is cached locally, and each load goes to the server. - A synchronous throw from
opennow becomes a rejected promise. A stuck or missing store falls back to the server, and the next attempt can open it normally. - In an app that uses
savedAnswers, the "no storage" case used to render no data. It now draws in about 0.6 s, and the other returning-user cases still pass.
Pull requests (1)
Full changelog: v0.1.132...v0.1.133
v0.1.132
Key takeaways
- [Breaking]
StatusMessagesno longer takesmax: In@columenlabs/lumen-ui, delete themaxprop from every<StatusMessages>you render, because the component now always shows a single line and TypeScript will flag any call that still passes it. - [Improvement] The status bar shows only the latest message:
StatusMessagesin@columenlabs/lumen-uirenders the first entry ofmessages(newest first). A new message replaces the one on screen, so two different errors no longer appear as two chips side by side. - [Improvement] Earlier messages sit behind the
+Nchip: InStatusMessages, every message except the newest is listed behind the+Nchip in the order it was posted. Errors are no longer sorted ahead of other messages, so the newest message always wins. - [Improvement] A wider status line: The
--status-message-maxCSS variable is nowmin(40rem, 45vw)from 768px and55vwbelow that, so the single line has more room. Override the variable if your layout needs a different width.
Shell
StatusMessagesshows one line: the newest message.- Older messages are reachable through the
+Nchip instead of being drawn next to the latest one. - Repeated errors still collapse into one line with a count (for example
×3), followed by+Nfor the other distinct messages. - On phones the line is one row, and a tap shows the full text.
- The
maxprop is removed. This is a breaking change for any caller that passes it. In an app that uses it, delete the prop.
Theme
--status-message-maxis wider:min(40rem, 45vw)from 768px and55vwbelow. The status line no longer shares its row with a second message.
Docs
- The contributor guidance for the status bar now says "latest message only".
Tooling
- Lint, type-check and the package build all pass on this change.
Pull requests (1)
Full changelog: v0.1.131...v0.1.132
v0.1.131
Key takeaways
- [Fix] Whole-number axes on stacked columns: In
@columenlabs/lumen-ui,StackedColumnsnow labels its value axis in whole steps of 1, 2 or 5 × 10ⁿ (never below 1) when every plotted value is a whole number, so a day with 2 API calls reads 0, 1, 2 instead of 0, 0.5, 1, 1.5, 2. - [Fix] Whole-number axes on the line chart: The line chart in
@columenlabs/lumen-uinow uses the sameniceTicksrule, so count data such as small usage totals no longer shows fractional ticks, while charts with fractional values are unchanged. - [Fix] One
mainlandmark per page: In@columenlabs/lumen-ui,PageBodynow renders adivinstead of a<main>, so pages inside the shell no longer nest<main>in the shell'sSidebarInset(which is already the page's<main>) and screen readers hear a single main landmark.
Charts
niceTicks, which sets the axis steps forStackedColumnsand the line chart, now picks whole steps of 1, 2 or 5 × 10ⁿ, never below 1, when all plotted values are whole numbers.- Charts with any fractional value keep their existing tick steps.
Layout and widgets
PageBodyrenders adivrather than a<main>, which removes invalid nested<main>elements and the doubled landmark.- This is not a breaking change. If an app styled or queried
PageBodyas amainelement, target thePageBodyelement another way.
Pull requests (1)
Full changelog: v0.1.130...v0.1.131
v0.1.130
Key takeaways
- [Feature]
fetchWithinputs a deadline on any request: From@columenlabs/lumen-ui, callfetchWithin(url, init, ms)to make a rawfetchreject with aTimeoutErroraftermsmilliseconds. Asignalyou pass ininitstill aborts it, and the newFETCH_MSconstant (15 s) is the default to pass. - [Fix] Built-in requests no longer wait forever: In
@columenlabs/lumen-ui, the app switcher, What's new, notifications (reads and writes), tasks and market data now give up afterFETCH_MS. The new-build check gives up after 5 s. A stalled network can no longer leave these hanging. The assistant's chat stream deliberately keeps the caller'ssignal, since a stream can legitimately run for a long time. - [Improvement] Prod safety rules are now concrete: The repo's contributor guide now says where timeouts belong (
savedAnswers,API_TIMEOUT_MS,fetchWithin, andAbortSignal.timeoutin worker code). It also says that fixing up old data on read should go throughwaitUntil, and that a changed JSON shape gets a new key version. The review checklist now blocks any pull request that breaks one of these rules.
Nothing here is breaking, and there is no visual change.
Shell
- The app switcher, What's new, notifications, tasks and market data requests now use
fetchWithinwith the 15 sFETCH_MSbound. - The new-build check uses a shorter 5 s bound, because it runs in the background and should fail fast.
- A caller's own
signalstill cancels these requests as before. - The assistant's chat stream is intentionally left unbounded by the timeout and keeps the caller's signal.
Docs
- The Prod safety section of the contributor guide now spells out the rule "every wait has a timeout".
- It lists where the bounds live:
savedAnswers,API_TIMEOUT_MS,fetchWithin, andAbortSignal.timeoutin worker code. - It says that fix-ups of old data on read go to
waitUntil. - It says that a new JSON shape gets a new key version.
- It lists where the bounds live:
Tooling
- The review checklist now treats a break of any Prod safety rule as a reason to block a pull request.
Pull requests (1)
Full changelog: v0.1.129...v0.1.130
v0.1.129
Key takeaways
- [Improvement] Clearer Admins tile caption: In
LumenPeoplefrom@columenlabs/lumen-ui, the Admins tile caption now reads "Can’t remove the last admin" instead of "at least 1 always kept", so your users see the rule in plain words with no prop or config change.
Layout and widgets
- The Admins tile in
LumenPeoplenow says "Can’t remove the last admin" where it used to say "at least 1 always kept". - This is a copy change only. The layout and props are unchanged, so there is nothing to update in your app. If you match on the old caption text in tests or snapshots, update it.
Pull requests (1)
Full changelog: v0.1.128...v0.1.129
v0.1.128
Key takeaways
- [Fix] Requests no longer hang forever:
apiJsonfrom @columenlabs/lumen-ui now gives up after 15 s for reads and 60 s for writes, so a request the network never answers no longer leaves a page loading indefinitely. - [Feature] Set a timeout per call: pass
timeoutMsin theinitargument ofapiJsonto override the limit for one request, or import the exportedAPI_TIMEOUT_MSconstant to see the defaults. - [Improvement] Timeouts land in your existing error states: when
apiJsontimes out it throws anApiErrorwith status 0 ("The server didn't answer in time …"), which is reported and counted like a network failure, so the error and empty states you already handle take over without code changes.
Shell
apiJsonnow has a timeout. Reads wait up to 15 s and writes up to 60 s, because writes may be doing slow work the person asked for.API_TIMEOUT_MSis now exported and holds the default limits.- A timeout throws
ApiErrorwith status 0, so code that already handles network failures needs no change. init.timeoutMssets a different limit for a single call.- A
signalyou pass in still aborts the request. That abort is still rethrown as anAbortErrorfor you to handle, and it is not treated as a timeout. - This is not a breaking change. The signature only gains an optional field.
Pull requests (1)
Full changelog: v0.1.127...v0.1.128