Reference
Feature SDK changelog
@roughapp/feature, why it matters, and what to do
when upgrading.The SDK is pre-1.0, so minor releases can contain breaking changes.
v0.6.0
We have been tidying up our codebase this week and renaming some
internal components to be more consistent. We have tried to minimise
the number of breaking changes, but there are a few minor adjustments
you will need to make in your apps.
Surfaces now use toolList (previously tools) and we have removed the custom error classes to
keep things simple.
On the new features side, you can now specify a custom prompt for your
Rough Project, which can guide how Rough builds Features, helping them
feel even more at home in your product!
Breaking changes
- The Surface
toolsproperty has been renamed totoolList. New code should passtoolListtodefineRoughSurface(), and readsurface.toolListinstead. To make this transition easier, thedefineRoughSurface()function will continue to accepttools, but we highly recommend that you refactor your code to usetoolList, as we will be removing this option in a future version.Migration steps…
- Replace the
toolsoption in each call todefineRoughSurface()withtoolList. - Replace any reads of
surface.toolswithsurface.toolList. - Run your type checks and tests to catch Surface definitions or helpers that still use the old property.
// Before const surface = defineRoughSurface({ key: 'dashboard', name: 'Dashboard', description: 'The main dashboard view', tools: [getCurrentUser], }) console.log(surface.tools) // After const surface = defineRoughSurface({ key: 'dashboard', name: 'Dashboard', description: 'The main dashboard view', toolList: [getCurrentUser], }) console.log(surface.toolList) - Replace the
- Our custom error classes have been removed. The SDK now throws plain old
Errorvalues. This keeps error handling simpler, but if you were importing one of our customRough*Errorclasses, you will need to change your code.Migration steps…
- Remove imports of
RoughClientDestroyError,RoughClientDestroyedError,RoughInvalidClientError,RoughReplicacheIdentityConflictError, andRoughSurfaceContractConflictError. - Replace
instanceof Rough*Errorbranches with your app's normal error reporting or recovery flow. - Stop reading the removed
errors,replicacheName, andsurfaceKeyfields. Display the regular error message where useful, but do not depend on matching its text for application logic.
// Before import { RoughClientDestroyedError, RoughSurfaceContractConflictError, } from '@roughapp/feature' try { await openRoughCreate({ client, surface }) } catch (error) { if (error instanceof RoughClientDestroyedError) { // ... } } // After try { await openRoughCreate({ client, surface }) } catch (error) { const message = error instanceof Error ? error.message : String(error) reportError(message) } - Remove imports of
Added
- Rough Projects can now have custom prompts! Rough admins can specify a custom system prompt for their Rough Features. This lets you guide how the Features should fit into your product by specifying conventions or particular styles for generated Rough Features.
v0.5.0
Users can now upload images to Feature Builder chats, and when embedding a Rough Feature, you can now hide Rough's editing controls. This release also removes the <rough-surface> component and cleans up the styling of the <rough-feature> frame.
Breaking changes
<rough-surface>has been removed. Instead of using Rough to render a list of surfaces, we encourage you to control how features are rendered. You can use thegetRoughFeatures()function to access the feature list, and then render each using<rough-feature>. This gives you full control over how and where features are rendered for a Surface.Migration Guide…
// Before: <rough-surface> rendered every published feature const surfaceElement = document.createElement('rough-surface') surfaceElement.client = client surfaceElement.surface = surface slot.append(surfaceElement) // After: subscribe and render each feature in host-owned layout const subscription = getRoughFeatures({ client, surface, onFeatures: (features) => { slot.replaceChildren( ...features.flatMap((feature) => { if (!feature.publishedSpriteBuildId) return [] const featureElement = document.createElement('rough-feature') featureElement.client = client featureElement.surface = surface featureElement.featureId = feature.id featureElement.buildId = feature.publishedSpriteBuildId return [featureElement] }), ) }, }) // When this part of the UI unmounts await subscription.unsubscribe()<rough-feature>no longer has visible border styles. This makes it easier to integrate Rough Features into your app aesthetically, allowing you to control how the feature frame looks.
Added
- Users can upload images. Each message can now have up to three GIF, JPEG, PNG, or WebP images attached. These images can be used to influence the feature design or even be integrated and displayed in the feature directly.
- The new
isEditableprop hides the Rough Edit button. WhenisEditableis set tofalse, users will no longer see the Rough Icon button and will not be able to customise that feature.
v0.4.2
The Feature Builder's chat assistant can now use build logs when helping users refine a feature, and open clients receive more reliable real-time updates.
Added
- The chat assistant can inspect the logs for a specific build. It can use build steps, failures and agent output as context when responding to follow-up messages.
Changed
- Real-time updates now cover changes made across Rough’s services. Publishing from the management app and progress from background build workers notify connected Feature SDK clients over their project WebSocket, rather than waiting for the next poll.
- Fallback polling now runs once per minute instead of every five seconds. This reduces background requests while the WebSocket is connected.
Fixed
- The Build Logs panel now follows the latest build in the conversation. Selecting an earlier ready build for preview no longer replaces the logs for the build being discussed.
v0.4.1
Documentation-only release. The README now has expanded setup, security, lifecycle, theming and API guidance, and the changelog includes the release notes that were missing from 0.4.0. There are no runtime, type or API changes.
v0.4.0
The Feature Builder is now conversational: Rough can clarify what to build, inspect the tools available on the surface and start a build when it has enough context. The recommended client and surface APIs are unchanged, so host apps do not need an integration migration.
Added
- Feature Builder chat. Conversations are saved per feature and remain available when the builder is reopened. Follow-up messages can refine the feature before Rough starts another build.
- Markdown responses. Assistant messages render headings, lists, links, tables, blockquotes and code blocks.
- Build cancellation. Pending and in-progress builds have a Stop control. Canceled builds remain in the conversation and build log with the time they ran before stopping.
Changed
- Build and publish updates reach open clients sooner. The SDK uses an authenticated WebSocket when available, so updates no longer wait for the five-second polling interval. Polling remains available as a fallback.
- Abandoned create flows no longer leave empty features behind. Rough creates the feature only after the user sends the first message.
- Advanced consumers of the incidental
SpriteBuildexport must handle the newCANCELEDstatus. Build records also includecancelRequestedAt: number | null; update exhaustive status switches and test fixtures accordingly.
v0.3.0
This release has breaking API changes. Rough now uses an explicit client for each project instead of global initialization. Apps can safely show features from multiple Rough projects at the same time, and each client has a clear, awaitable lifecycle.
Breaking changes
The recommended migration is shown below. In summary:
- All stateful functions and components now take a client. Pass
clientandsurfacetogetRoughFeatures(),openRoughCreate(),<rough-surface>,<rough-feature>,<rough-edit-button>and the modal elements. - Cleanup methods are asynchronous and safe to call more than once. Await
client.destroy(),subscription.unsubscribe()andmodal.close()when you need to know that cleanup has finished. - Create one client and share it for each signed-in person and project. A second client for the same
baseUrl,projectIdand person fails with an error. Clients for different projects or different signed-in people can run together. openRoughCreate()no longer requires a mounted Rough component. You can open it directly with a client and surface.
Added
createRoughClient({ projectId, baseUrl?, fetchUserToken })creates and starts a client for one project. Keep the client for as long as your app needs that project, then callawait client.destroy().whenRoughClientReady({ client })lets you wait for startup or handle a startup error.openRoughCreate({ target })lets you attach the create modal inside the element that scopes your Rough theme. It defaults todocument.body.
Removed
initRough()has been removed. There is no default or global client.defineSurface()has been replaced bydefineRoughSurface(). RenametoolListtotoolswhen updating your surface definitions.registerSurfaceEntryhas been removed. You no longer need to register a surface before using it.
Migration
// Before
initRough({ projectId, fetchUserToken })
const surface = defineSurface({ key, name, description, toolList })
const unsubscribe = getRoughFeatures(surface, onFeatures)
openRoughCreate(surface, { projectId })
// After
const client = createRoughClient({ projectId, fetchUserToken })
const surface = defineRoughSurface({ key, name, description, tools })
const subscription = getRoughFeatures({ client, surface, onFeatures })
const modal = await openRoughCreate({ client, surface })
await modal.close()
await subscription.unsubscribe()
await client.destroy()v0.2.1
The published package now includes this changelog, so release and migration notes are available alongside the installed SDK. There are no runtime, type or API changes.
v0.2.0
Upgrade from 0.1.0 as soon as you can. Version 0.1.0 relies on retired service endpoints and can no longer list, create, build or publish features. Version 0.2.0 uses the supported synchronization service.
Upgrading needs no configuration change. initRough takes
the same projectId, fetchUserToken and
optional baseUrl, and no export was added or removed.
Consumers of the exported data types or theme variables need the
migrations below.
Breaking changes
Hosts using exported data types or custom theme variables need the migrations below.
SpriteandSpriteBuildreference related records by ID instead of nesting them.// 0.1.0 type Sprite = { id: string name: string ownedByPersonId: string publishedSpriteBuild: SpriteBuild | null // full nested record } type SpriteBuild = { createdByPerson: { id: string; name: string } // ... } // 0.2.0 type Sprite = { id: SpriteId name: string ownedByPersonId: PersonId publishedSpriteBuildId: SpriteBuildId | null // ID only } type SpriteBuild = { createdByPersonId: PersonId // ... }Anything reading
sprite.publishedSpriteBuild.artifactUrl,.statusor.promptoff agetRoughFeaturesresult must be reworked, andSpriteBuildno longer carries the build author's display name. There is no replacement for that name in the public API.- ID fields are branded string types.
SpriteId,SpriteBuildId,SurfaceIdandPersonIdare nowstring & { __brand: … }rather thanstring, onRoughFeature'sfeatureIdandbuildId,RoughCreateModal's andSpriteBuildMenu'ssurfaceId,SpriteFrame'sspriteBuildId, andregisterSurfaceEntry'ssurfaceId.Reading is unaffected, because a branded string is still assignable to
string. Passing one in is not: an ID you hold as a plainstring, from your own database or a URL parameter, is rejected. The brand types are not themselves exported, so reach them through the types that are:type SpriteId = Sprite['id'] type SpriteBuildId = NonNullable<Sprite['publishedSpriteBuildId']> type SurfaceId = RoughCreateModalElement['surfaceId']IDs that flow straight out of
getRoughFeaturesinto a component still typecheck with no cast. - The stylesheet ships an expanded set of semantic design tokens. Hosts that theme Rough through CSS custom properties need to remap
these eight removed variables to their nearest replacement:
Removed in 0.2.0 Closest 0.2.0 token --rough-text--rough-foreground--rough-text-secondary--rough-muted-foreground--rough-surface--rough-card--rough-surface-border--rough-border--rough-background-muted--rough-muted--rough-danger--rough-destructive--rough-shadow--rough-shadow-xs,-md,-lg--rough-accent-secondaryNo equivalent; --rough-brandis the saturated brand colourWatch
--rough-accentin particular. The name survived but now controls a subtle hover background rather than the primary action colour. A host that set it to a brand colour to tint buttons will find it tinting hover states instead.--rough-primarynow controls primary actions.--rough-backgroundand--rough-borderalso kept their names with new values. The expanded set adds variables for radius and success states.
Changed
- Feature data now stays synchronized while the SDK is mounted. Published features and build changes can appear without another host action. Unlike 0.1.0's one-off requests, 0.2.0 keeps a sync loop open and polls every five seconds, which may affect network monitoring or request allowlists.
getRoughFeaturesis a live subscription rather than a one-shot fetch. In 0.1.0 the callback fired once and only fired again when something calledtriggerRefresh. It now re-fires on every relevant change that syncs in, so a feature published in another tab or by another user appears without the host doing anything.- The JavaScript bundle is larger because it now includes continuous sync.
index.jsgrows from 105 kB to 320 kB raw (27 kB to 77 kB gzipped). The stylesheet grows from 324 B to 5.2 kB with the expanded theme token set.
Removed
onpublishis gone fromRoughCreateModal,RoughEditButtonandRoughEditModal. BecausegetRoughFeaturesis now a live subscription, a publish already reaches the host through that callback, which is what the prop existed to signal. Move any publish handling into thegetRoughFeaturescallback. Note that the prop was optional, so depending on your setup this may fail quietly rather than at compile time.triggerRefreshis gone from theSurfaceEntrypassed toregisterSurfaceEntry. It was the manual refresh hook for the old fetch-once model and has nothing to do now. Delete it from the object you pass in; leaving it there is an excess property and will be rejected.
Added
- A light and dark mode toggle in the Feature Builder. The stylesheet
now carries a
.rough-darkclass holding the dark values for every token. - The Feature Builder's build log now reports token usage and estimated cost per agent step, formatted in cents below one dollar.
v0.1.0
Initial public release of the Rough Feature SDK. This version is no longer supported because the service endpoints it uses have been retired; upgrade to a current release before integrating it.
Added
- An embeddable browser SDK for Rough Features. The npm package ships compiled ESM, TypeScript declarations, Web Components and a separate stylesheet for apps using a modern bundler.
- Project and user authentication.
initRough()configures a project with a host-providedfetchUserTokencallback and an optional API base URL. - Typed surface definitions.
defineSurface()describes where features can appear and theQuery,MutationandSubscriptiontools they can call. - Published feature rendering and discovery. Apps can
use
RoughSurfaceor<rough-surface>to render features andgetRoughFeatures()to receive the published feature list for a surface. - Embedded feature creation and editing.
openRoughCreate()opens the Feature Builder, where users can create, build and publish a feature without leaving the host product.