All four packages in this repo (@tylerwarburton/sprite-core,
@tylerwarburton/sprite-core-client, ai.openclaw.spritecore:sprite-core-client
and -android, SpriteCoreClient) release together at one version. Tag
format: v<version> (e.g. v1.0.0).
- Kotlin: new
:composeGradle module publishingai.openclaw.spritecore:sprite-core-client-compose. Shipsai.openclaw.spritecore.client.compose.CharacterAvatar, a Compose Composable that wiresAnimationGraph+SpriteAnimationPlayer+BitmapFrameSourceinto a drop-in widget. Takes abitmapTransform: (Bitmap) -> Bitmaphook so platform-specific framing (e.g. a watch face cropping a full-body sprite to a headshot) lives at the call site rather than inside each consuming app. - Swift:
CGImageFrameSource(atlas-aware ImageIO-backedFrameSourcemirroring Kotlin'sBitmapFrameSource) andCharacterAvatarView(SwiftUI View with acgImageTransformhook). Gated oncanImport(SwiftUI) && canImport(ImageIO). - TypeScript:
ImageBitmapFrameSource(atlas-awareFrameSource<AtlasFrame>with async prefetch) andmountCharacterAvatar(canvas, opts)returning a{ setState, dispose }controller. Framework-free — drives anHTMLCanvasElementdirectly via 2D context.
- Plugin:
node.getCharacterManifestgateway RPC is now registered with{ scope: "operator.read" }. Without an explicit scope, upstream openclaw (>=2026.5.0) defaults unscoped methods tooperator.admin, which silently rejected every phone/watch call withINVALID_REQUEST: missing scope: operator.admin. The kit'sfetchManifestreturned null, so sprites never rendered on the phone — even though the gateway connection, TTS, STT and the dashboard sprite UI all kept working (they ride HTTP routes, not RPC scope). Sibling RPCsprite-core.agentsalready opts down tooperator.read; this RPC was missed during the 0.5.5 fork→upstream port and the regression only surfaced after the host-default-deny took effect.
- Plugin compat: works on upstream openclaw
>=2026.5.0. BumpspeerDependencies.openclawfrom>=2026.4.10to>=2026.5.0and updates the dev pin to^2026.5.6.
- Plugin now declares
activation.onStartup: trueinopenclaw.plugin.jsonso upstream's plugin loader actually invokesregister()at gateway boot. Without it, the plugin was loaded but its HTTP routes (/sprite-core/ui,/sprite-core/agents,/openclaw-assets, etc.) were never registered — every URL fell through to the Control SPA catch-all. api.registerSystemPromptContribution(...)is now wrapped in atypeof === "function"guard. Upstream openclaw 2026.5.x doesn't expose that method (fork-only API), and an unguarded call threwTypeErrorand aborted plugin registration. The plugin now gracefully skips the prompt-contribution feature on hosts that don't expose it; atlas/TTS/STT and UI continue to work. Driving sprite state markers from the client side (or via tools/RPC) remains unaffected.ui-distresolver now also probes<here>/../../ui-distand<here>/../../../ui-dist. Previous candidates only worked for the bundled (tsdown) layout and the source layout; the tsc-emitteddist/src/-nested layout (which is what the newbuildscript produces) needed two more candidates. Without this,/sprite-core/uireturned a 503 with the "SpriteCore UI bundle not built" message even whenui-dist/was correctly shipped.
- Added
buildandprepackscripts topackages/plugin/package.json.prepackrunspnpm run build:ui && tscsonpm packproduces a self-contained tarball with bothdist/andui-dist/populated, even on a fresh clone where neither directory exists yet. - Added
distto the plugin'sfilesarray so the build output ships in the published tarball alongside the existingui-dist.
- Workspace layout:
packages/plugin,packages/client-js,packages/client-kotlin,packages/client-swift, plus sharedschema/andfixtures/. - Cross-language client SDKs (TypeScript, Kotlin, Swift) implementing the
CharacterManifestwire protocol:AnimationGraphprojection, coroutine / async-iterator /actorsprite player,FrameSourceadapter seam, streaming<<<state>>>/<<<state-N>>>marker parser, asset cache. schema/package publishing TypeBox definitions as the single source of truth for wire types (downstream Kotlin and Swift types kept in lockstep via thefixtures/conformance suite).- Release workflow publishing all four artifacts from a single
v*tag:@tylerwarburton/sprite-core→ npm (GitHub Packages)@tylerwarburton/sprite-core-client→ npm (GitHub Packages)ai.openclaw.spritecore:sprite-core-client+-android→ Maven (GitHub Packages)SpriteCoreClient— consumed via git tag by SwiftPM
scripts/check-versions.mjs— pre-flight gate that fails the release if any package's declared version disagrees with the tag.
- Plugin moved from repo root into
packages/plugin/. Package name andinstall.npmSpecunchanged — operators continue to install@tylerwarburton/sprite-corevia the existing instructions.
- TypeScript marker parser upgraded to match Kotlin semantics (
<<<state-N>>>play-count suffix). All three language ports now produce identical parse results for the same inputs. emotionsfield added to the KotlinCharacterManifestdata class (previously drifted from the TypeScript wire schema).- Dashboard atlas previews now render the cropped source rect via canvas
drawImageinstead of the entire atlas sheet — atlas-kind avatars actually animate when you press play. - Dashboard tracks the OpenClaw Control UI's persisted theme (light / dark)
by mirroring
data-theme/data-theme-modeattributes onto its own document, withstorageevent +prefers-color-schemefallback. pnpm typecheckis green at the repo root (three pre-existing errors inpackages/pluginblocked the documented quickstart).