Skip to content

v0.23.1

Choose a tag to compare

@donalddellapietra donalddellapietra released this 17 Aug 08:14
· 24 commits to main since this release

Patch Changes

  • Chat writes can no longer resurrect a deleted chat, the delete cascade is bounded, and cookie-only server-action calls get a 401 instead of a 500. updateChat and appendMessage now re-check the chat through getChat and return false instead of writing when it is gone (records.update/records.create are upserts, so an unguarded post-stream write recreated a deleted chat as a title-less ghost). New records.deleteWhere tool on the room tools API — { collection, where, limit } → { deleted }, bounded per call, same delete permission check as records.delete and refusing the whole page if any match is denied — lets deleteChatCascade spend one subrequest per page instead of one per message. The scaffolded POST /api/actions/:name reads the bearer token defensively: an authenticated caller with no Authorization header is now refused 401 rather than throwing, and the file carries the trust-model note that action tools execute as the caller with RBAC off.
  • CLI honesty pass: deepspace test run's default suite now names every spec file it skipped (prose line + skippedSpecs in --json); push refuses a GitHub-sourced app in its preflight, naming the repository, instead of reconstructing a repository-less sentence from git's discarded 422; push's no_commits refusal and app init --new-id both name their next step; and a scaffold whose identity registered no longer tells you to auth login.
  • deploy settles two refusals before it builds and uploads: a collaborator whose app has no live APP_OWNER_JWT is refused with the platform's own sentence and a new owner_jwt_missing code (the deploy worker's commit-time 409 now carries that code too), and a changed wrangler name is confirmed or refused (rename_required) from the registered host instead of after the upload.
  • Verification-pass fixes: records.deleteWhere refuses a where whose keys name no field (an unknown key used to be silently dropped, deleting an unfiltered page), refuses schemaless collections and non-numeric limits, and pages after the RBAC read filter; a users schema with an explicit read: false keeps an empty roster for that role, and a role change now refreshes every connected roster; the scaffold's chat route stops (rather than continuing) when a mid-stream delete makes appendMessage return false, and PATCH /api/ai/chats/:id answers 404 for a write that never landed; the build plugin's .dev.vars sweep clears every worker dir before reporting an unsafe one; a missing wrangler.toml is not_in_app_repo with a remedy instead of invalid_config, and detectAppName shares the one reader; push's no_commits names the __APP_ID__ case only when the placeholder is present (with the app init action); app init ships a git commit … wrangler.toml action when it did not commit; secrets refusals keep the server's code in --json; app list shows a renamed app's reserved old name.
  • AX-pass fixes: secrets reads on an unregistered app id refuse app_not_registered instead of answering an empty list; the scaffold registers its AI chat routes only when the copilot schemas are present (they could only 500 without them) and names the valid model ids (unknown_model) on a bad modelId; create-deepspace says which plane it registered the app on and which id; deploy/push/pull/update/workspace share one not_in_app_repo sentence; lintSchemas warns when a role uses the 'team' level with no team_members collection; an implausible command guess is no longer handed back as an executable action and logout counts as destructive; transfer accept names collaborators inherited from the previous owner; secrets set/upload/delete --json carry appliesAtDeploy: true; rollback --help states secrets are kept; app update leaves a file:/link: deepspace spec alone.
  • Review follow-ups: executeQuery's limit now counts the records the caller receives — when a per-row read filter applies it scans in bounded batches (a 5,000-row scan budget per call — worst case one batch of max(limit, 200) rows past it, and a limit above the budget is honored as asked — reported to callers that ask, so limit stays a bound on work) instead of letting a SQL LIMIT end the result early, and read: 'own' / read: true push into SQL like 'team' already did (only when the declared field resolves to a real column — a misdeclared one falls back to the per-row check instead of throwing) (so records.deleteWhere pages boundedly under RBAC, and a partially-failed page is refused rather than read as "no more matches"); a new roster: 'read-policy' users-schema option scopes the user.list roster to the caller's read policy for tenant/team-partitioned apps; ranked command guesses are never handed back as executable actions (only an exact quoted path is); classifyPushTransportFailure keeps a last-resort 422 → source_managed_by_github fallback; transfer accept reports inheritedCollaborators on replays too; app init offers its git commit action only when wrangler.toml is actually uncommitted; one anonymous-identity helper (isAnonymousUserId).
  • The users roster (useUsers()) now updates live: a room pushes user.list to every other connected socket when a user registers for the first time or changes name, avatar, or role — the client only asked once per connection, so a peer who joined after a tab connected rendered as "Unknown" there until reload.
  • app list now surfaces incoming ownership offers and an undeployed app's reserved name, transfer accept reports a replayed acceptance as a replay instead of a fresh handshake, and transfer status/offer name the offerer and say that acceptance ends the offerer's access
  • Fix useUsers() returning an empty roster (chat authors rendered as "Unknown") in rooms without an app users schema: the users list now returns every registered user's public identity to non-admin callers instead of filtering by the schema's row policy, which still guards full-row reads.
  • One wrangler.toml reader behind the CLI and deepspace/build — one error shape (WranglerConfigError; a missing file is not_in_app_repo, a broken one invalid_config). New refusal, duplicate_app_id: a wrangler.toml that sets the same DEEPSPACE_APP_ID under more than one section ([vars] and [env.<name>.vars]) is refused by every CLI command and by vite build — each environment is its own app; run deepspace app init --env <name> to mint one for it. test accounts recover restores the display name the platform now returns.